Update app.py
Browse files
app.py
CHANGED
|
@@ -204,7 +204,10 @@ CATALOG_TEMPLATE = '''
|
|
| 204 |
.quantity-control { display: flex; align-items: center; background: var(--bg); border-radius: 8px; overflow: hidden; border: 1px solid var(--border); }
|
| 205 |
.quantity-control button { border: none; background: transparent; width: 32px; height: 32px; font-size: 1.1rem; cursor: pointer; color: var(--primary); display: flex; align-items: center; justify-content: center; transition: background 0.2s; }
|
| 206 |
.quantity-control button:active { background: #e0e0e0; }
|
| 207 |
-
.quantity-control input { width: 36px; height: 32px; border: none; text-align: center; background: transparent; font-weight: 600; font-size: 0.95rem;
|
|
|
|
|
|
|
|
|
|
| 208 |
.box-btn { background: var(--primary); color: #fff; border: none; border-radius: 8px; padding: 0 10px; height: 32px; font-size: 0.8rem; font-weight: 600; cursor: pointer; transition: opacity 0.2s; }
|
| 209 |
.box-btn:active { opacity: 0.8; }
|
| 210 |
|
|
@@ -232,7 +235,10 @@ CATALOG_TEMPLATE = '''
|
|
| 232 |
.cart-item-controls { display: flex; align-items: center; background: var(--surface); border-radius: 8px; border: 1px solid var(--border); overflow: hidden; }
|
| 233 |
.cart-item-controls button { border: none; background: transparent; width: 30px; height: 30px; font-size: 1rem; cursor: pointer; color: var(--primary); }
|
| 234 |
.cart-item-controls button:active { background: #e0e0e0; }
|
| 235 |
-
.cart-item-controls
|
|
|
|
|
|
|
|
|
|
| 236 |
.cart-item-price { font-weight: 700; color: var(--primary); min-width: 70px; text-align: right; }
|
| 237 |
.cart-item-delete { color: #ff7675; background: none; border: none; font-size: 1.1rem; cursor: pointer; padding: 5px; }
|
| 238 |
|
|
@@ -446,7 +452,7 @@ CATALOG_TEMPLATE = '''
|
|
| 446 |
${addBoxBtn}
|
| 447 |
<div class="quantity-control">
|
| 448 |
<button onclick="updateCart('${p.product_id}', -1)"><i class="fas fa-minus" style="font-size:0.8rem;"></i></button>
|
| 449 |
-
<input type="
|
| 450 |
<button onclick="updateCart('${p.product_id}', 1)"><i class="fas fa-plus" style="font-size:0.8rem;"></i></button>
|
| 451 |
</div>
|
| 452 |
</div>
|
|
@@ -499,6 +505,12 @@ CATALOG_TEMPLATE = '''
|
|
| 499 |
updateCartUI();
|
| 500 |
}
|
| 501 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 502 |
function updateCartUI() {
|
| 503 |
let total = 0;
|
| 504 |
for (let id in cart) {
|
|
@@ -537,7 +549,7 @@ CATALOG_TEMPLATE = '''
|
|
| 537 |
<div style="display:flex; align-items:center; gap: 10px;">
|
| 538 |
<div class="cart-item-controls">
|
| 539 |
<button onclick="updateCart('${id}', -1)"><i class="fas fa-minus" style="font-size:0.8rem;"></i></button>
|
| 540 |
-
<
|
| 541 |
<button onclick="updateCart('${id}', 1)"><i class="fas fa-plus" style="font-size:0.8rem;"></i></button>
|
| 542 |
</div>
|
| 543 |
<button class="cart-item-delete" onclick="updateCart('${id}', 0, 0)"><i class="fas fa-trash-alt"></i></button>
|
|
@@ -692,12 +704,16 @@ ORDER_TEMPLATE = '''
|
|
| 692 |
.total-row { background: #fafafa; font-weight: 800; }
|
| 693 |
.total-row td { font-size: 1.1rem; border-bottom: none; }
|
| 694 |
|
| 695 |
-
.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 696 |
|
| 697 |
-
.
|
| 698 |
-
.
|
| 699 |
-
.edit-btn:active { background: #dfe6e9; }
|
| 700 |
-
.delete-btn { color: #ff7675; }
|
| 701 |
|
| 702 |
.action-bar { position: fixed; bottom: 0; left: 0; width: 100%; background: var(--surface); box-shadow: 0 -4px 20px rgba(0,0,0,0.08); padding: 15px 20px calc(15px + env(safe-area-inset-bottom)); display: flex; gap: 15px; z-index: 100; justify-content: center; border-top-left-radius: 20px; border-top-right-radius: 20px; }
|
| 703 |
.action-bar-inner { display: flex; gap: 15px; width: 100%; max-width: 900px; }
|
|
@@ -715,7 +731,8 @@ ORDER_TEMPLATE = '''
|
|
| 715 |
.table-responsive { border: none; overflow: visible; }
|
| 716 |
table { min-width: 100%; }
|
| 717 |
th, td { border: 1px solid #000; }
|
| 718 |
-
.action-bar, .
|
|
|
|
| 719 |
}
|
| 720 |
@media (max-width: 600px) {
|
| 721 |
.header h1 { font-size: 1.4rem; }
|
|
@@ -758,7 +775,6 @@ ORDER_TEMPLATE = '''
|
|
| 758 |
<th style="text-align: left;">Наименование</th>
|
| 759 |
<th>Фото</th>
|
| 760 |
<th>Кол-во</th>
|
| 761 |
-
<th class="no-print">Действия</th>
|
| 762 |
<th>Цена</th>
|
| 763 |
<th>Сумма</th>
|
| 764 |
</tr>
|
|
@@ -766,27 +782,38 @@ ORDER_TEMPLATE = '''
|
|
| 766 |
<tbody>
|
| 767 |
{% for item in order.cart %}
|
| 768 |
{% set ppb = item.pieces_per_box|default(1)|int %}
|
| 769 |
-
{% set boxes =
|
| 770 |
{% set remainder = item.quantity % ppb %}
|
| 771 |
<tr>
|
| 772 |
<td>{{ loop.index }}</td>
|
| 773 |
-
<td style="text-align: left; font-weight: 500;">
|
| 774 |
-
{{ item.name }}
|
| 775 |
-
<span class="qty-info">
|
| 776 |
-
{% if ppb > 1 and boxes > 0 %}
|
| 777 |
-
{{ boxes }} кор.{% if remainder > 0 %} {{ remainder }} шт.{% endif %}
|
| 778 |
-
{% else %}
|
| 779 |
-
{{ item.quantity }} шт.
|
| 780 |
-
{% endif %}
|
| 781 |
-
</span>
|
| 782 |
-
</td>
|
| 783 |
<td class="img-cell"><img src="{{ item.photo_url }}" alt="img"></td>
|
| 784 |
-
<td style="
|
| 785 |
-
|
| 786 |
-
|
| 787 |
-
|
| 788 |
-
|
| 789 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 790 |
</div>
|
| 791 |
</td>
|
| 792 |
<td>{{ item.price }}</td>
|
|
@@ -794,7 +821,7 @@ ORDER_TEMPLATE = '''
|
|
| 794 |
</tr>
|
| 795 |
{% endfor %}
|
| 796 |
<tr class="total-row">
|
| 797 |
-
<td colspan="
|
| 798 |
<td>{{ order.total_price }} {{ currency_code }}</td>
|
| 799 |
</tr>
|
| 800 |
</tbody>
|
|
@@ -837,6 +864,30 @@ ORDER_TEMPLATE = '''
|
|
| 837 |
document.getElementById('loadingOverlay').style.display = 'none';
|
| 838 |
});
|
| 839 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 840 |
</script>
|
| 841 |
</body>
|
| 842 |
</html>
|
|
@@ -1196,6 +1247,7 @@ def edit_order(order_id):
|
|
| 1196 |
req_data = request.get_json()
|
| 1197 |
product_id = req_data.get('product_id')
|
| 1198 |
change = req_data.get('change', 0)
|
|
|
|
| 1199 |
remove = req_data.get('remove', False)
|
| 1200 |
|
| 1201 |
for item in order['cart']:
|
|
@@ -1203,7 +1255,11 @@ def edit_order(order_id):
|
|
| 1203 |
if remove:
|
| 1204 |
order['cart'].remove(item)
|
| 1205 |
else:
|
| 1206 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1207 |
if item['quantity'] <= 0:
|
| 1208 |
order['cart'].remove(item)
|
| 1209 |
break
|
|
@@ -1367,4 +1423,4 @@ if __name__ == '__main__':
|
|
| 1367 |
threading.Thread(target=periodic_backup, daemon=True).start()
|
| 1368 |
|
| 1369 |
port = int(os.environ.get('PORT', 7860))
|
| 1370 |
-
app.run(host='0.0.0.0', port=port)
|
|
|
|
| 204 |
.quantity-control { display: flex; align-items: center; background: var(--bg); border-radius: 8px; overflow: hidden; border: 1px solid var(--border); }
|
| 205 |
.quantity-control button { border: none; background: transparent; width: 32px; height: 32px; font-size: 1.1rem; cursor: pointer; color: var(--primary); display: flex; align-items: center; justify-content: center; transition: background 0.2s; }
|
| 206 |
.quantity-control button:active { background: #e0e0e0; }
|
| 207 |
+
.quantity-control input { width: 36px; height: 32px; border: none; text-align: center; background: transparent; font-weight: 600; font-size: 0.95rem; color: var(--primary); outline: none; }
|
| 208 |
+
.quantity-control input[type="number"]::-webkit-inner-spin-button,
|
| 209 |
+
.quantity-control input[type="number"]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
|
| 210 |
+
.quantity-control input[type="number"] { -moz-appearance: textfield; }
|
| 211 |
.box-btn { background: var(--primary); color: #fff; border: none; border-radius: 8px; padding: 0 10px; height: 32px; font-size: 0.8rem; font-weight: 600; cursor: pointer; transition: opacity 0.2s; }
|
| 212 |
.box-btn:active { opacity: 0.8; }
|
| 213 |
|
|
|
|
| 235 |
.cart-item-controls { display: flex; align-items: center; background: var(--surface); border-radius: 8px; border: 1px solid var(--border); overflow: hidden; }
|
| 236 |
.cart-item-controls button { border: none; background: transparent; width: 30px; height: 30px; font-size: 1rem; cursor: pointer; color: var(--primary); }
|
| 237 |
.cart-item-controls button:active { background: #e0e0e0; }
|
| 238 |
+
.cart-item-controls input { width: 35px; text-align: center; font-weight: 600; font-size: 0.9rem; border: none; background: transparent; color: var(--primary); outline: none; }
|
| 239 |
+
.cart-item-controls input[type="number"]::-webkit-inner-spin-button,
|
| 240 |
+
.cart-item-controls input[type="number"]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
|
| 241 |
+
.cart-item-controls input[type="number"] { -moz-appearance: textfield; }
|
| 242 |
.cart-item-price { font-weight: 700; color: var(--primary); min-width: 70px; text-align: right; }
|
| 243 |
.cart-item-delete { color: #ff7675; background: none; border: none; font-size: 1.1rem; cursor: pointer; padding: 5px; }
|
| 244 |
|
|
|
|
| 452 |
${addBoxBtn}
|
| 453 |
<div class="quantity-control">
|
| 454 |
<button onclick="updateCart('${p.product_id}', -1)"><i class="fas fa-minus" style="font-size:0.8rem;"></i></button>
|
| 455 |
+
<input type="number" id="qty-${p.product_id}" value="${qty}" onchange="manualUpdateCart('${p.product_id}', this.value)">
|
| 456 |
<button onclick="updateCart('${p.product_id}', 1)"><i class="fas fa-plus" style="font-size:0.8rem;"></i></button>
|
| 457 |
</div>
|
| 458 |
</div>
|
|
|
|
| 505 |
updateCartUI();
|
| 506 |
}
|
| 507 |
|
| 508 |
+
function manualUpdateCart(productId, val) {
|
| 509 |
+
let num = parseInt(val);
|
| 510 |
+
if (isNaN(num) || num < 0) num = 0;
|
| 511 |
+
updateCart(productId, 0, num);
|
| 512 |
+
}
|
| 513 |
+
|
| 514 |
function updateCartUI() {
|
| 515 |
let total = 0;
|
| 516 |
for (let id in cart) {
|
|
|
|
| 549 |
<div style="display:flex; align-items:center; gap: 10px;">
|
| 550 |
<div class="cart-item-controls">
|
| 551 |
<button onclick="updateCart('${id}', -1)"><i class="fas fa-minus" style="font-size:0.8rem;"></i></button>
|
| 552 |
+
<input type="number" value="${item.quantity}" onchange="manualUpdateCart('${id}', this.value)">
|
| 553 |
<button onclick="updateCart('${id}', 1)"><i class="fas fa-plus" style="font-size:0.8rem;"></i></button>
|
| 554 |
</div>
|
| 555 |
<button class="cart-item-delete" onclick="updateCart('${id}', 0, 0)"><i class="fas fa-trash-alt"></i></button>
|
|
|
|
| 704 |
.total-row { background: #fafafa; font-weight: 800; }
|
| 705 |
.total-row td { font-size: 1.1rem; border-bottom: none; }
|
| 706 |
|
| 707 |
+
.cart-item-controls { display: inline-flex; align-items: center; background: var(--surface); border-radius: 8px; border: 1px solid var(--border); overflow: hidden; margin-bottom: 5px; }
|
| 708 |
+
.cart-item-controls button { border: none; background: #f8f9fa; width: 30px; height: 30px; font-size: 1rem; cursor: pointer; color: var(--primary); transition: background 0.2s; }
|
| 709 |
+
.cart-item-controls button:active { background: #e0e0e0; }
|
| 710 |
+
.cart-item-controls input { width: 40px; text-align: center; font-weight: 600; font-size: 0.95rem; border: none; background: transparent; color: var(--primary); outline: none; }
|
| 711 |
+
.cart-item-controls input[type="number"]::-webkit-inner-spin-button,
|
| 712 |
+
.cart-item-controls input[type="number"]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
|
| 713 |
+
.cart-item-controls input[type="number"] { -moz-appearance: textfield; }
|
| 714 |
|
| 715 |
+
.screen-only { display: block; }
|
| 716 |
+
.print-only { display: none; }
|
|
|
|
|
|
|
| 717 |
|
| 718 |
.action-bar { position: fixed; bottom: 0; left: 0; width: 100%; background: var(--surface); box-shadow: 0 -4px 20px rgba(0,0,0,0.08); padding: 15px 20px calc(15px + env(safe-area-inset-bottom)); display: flex; gap: 15px; z-index: 100; justify-content: center; border-top-left-radius: 20px; border-top-right-radius: 20px; }
|
| 719 |
.action-bar-inner { display: flex; gap: 15px; width: 100%; max-width: 900px; }
|
|
|
|
| 731 |
.table-responsive { border: none; overflow: visible; }
|
| 732 |
table { min-width: 100%; }
|
| 733 |
th, td { border: 1px solid #000; }
|
| 734 |
+
.action-bar, .screen-only { display: none !important; }
|
| 735 |
+
.print-only { display: block !important; }
|
| 736 |
}
|
| 737 |
@media (max-width: 600px) {
|
| 738 |
.header h1 { font-size: 1.4rem; }
|
|
|
|
| 775 |
<th style="text-align: left;">Наименование</th>
|
| 776 |
<th>Фото</th>
|
| 777 |
<th>Кол-во</th>
|
|
|
|
| 778 |
<th>Цена</th>
|
| 779 |
<th>Сумма</th>
|
| 780 |
</tr>
|
|
|
|
| 782 |
<tbody>
|
| 783 |
{% for item in order.cart %}
|
| 784 |
{% set ppb = item.pieces_per_box|default(1)|int %}
|
| 785 |
+
{% set boxes = item.quantity // ppb %}
|
| 786 |
{% set remainder = item.quantity % ppb %}
|
| 787 |
<tr>
|
| 788 |
<td>{{ loop.index }}</td>
|
| 789 |
+
<td style="text-align: left; font-weight: 500;">{{ item.name }}</td>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 790 |
<td class="img-cell"><img src="{{ item.photo_url }}" alt="img"></td>
|
| 791 |
+
<td style="text-align: center;">
|
| 792 |
+
<div class="screen-only">
|
| 793 |
+
<div style="display:flex; flex-direction:column; align-items:center; gap:4px;">
|
| 794 |
+
<div style="display:flex; align-items:center; gap:8px;">
|
| 795 |
+
<div class="cart-item-controls">
|
| 796 |
+
<button onclick="updateItem('{{ item.product_id }}', -1)"><i class="fas fa-minus" style="font-size:0.7rem;"></i></button>
|
| 797 |
+
<input type="number" value="{{ item.quantity }}" onchange="manualUpdateOrder('{{ item.product_id }}', this.value)">
|
| 798 |
+
<button onclick="updateItem('{{ item.product_id }}', 1)"><i class="fas fa-plus" style="font-size:0.7rem;"></i></button>
|
| 799 |
+
</div>
|
| 800 |
+
<button onclick="updateItem('{{ item.product_id }}', 0, true)" style="color: #ff7675; background: none; border: none; font-size: 1.1rem; cursor: pointer; padding: 5px;"><i class="fas fa-trash-alt"></i></button>
|
| 801 |
+
</div>
|
| 802 |
+
<div style="font-size: 0.85rem; color: #00b894; font-weight: 600;">
|
| 803 |
+
{% if ppb > 1 and boxes > 0 %}
|
| 804 |
+
{{ boxes }} кор.{% if remainder > 0 %} {{ remainder }} шт.{% endif %}
|
| 805 |
+
{% else %}
|
| 806 |
+
{{ item.quantity }} шт.
|
| 807 |
+
{% endif %}
|
| 808 |
+
</div>
|
| 809 |
+
</div>
|
| 810 |
+
</div>
|
| 811 |
+
<div class="print-only" style="font-weight: bold;">
|
| 812 |
+
{% if ppb > 1 and boxes > 0 %}
|
| 813 |
+
{{ boxes }} кор.{% if remainder > 0 %} {{ remainder }} шт.{% endif %}
|
| 814 |
+
{% else %}
|
| 815 |
+
{{ item.quantity }} шт.
|
| 816 |
+
{% endif %}
|
| 817 |
</div>
|
| 818 |
</td>
|
| 819 |
<td>{{ item.price }}</td>
|
|
|
|
| 821 |
</tr>
|
| 822 |
{% endfor %}
|
| 823 |
<tr class="total-row">
|
| 824 |
+
<td colspan="5" style="text-align: right; padding-right: 20px;">Итого:</td>
|
| 825 |
<td>{{ order.total_price }} {{ currency_code }}</td>
|
| 826 |
</tr>
|
| 827 |
</tbody>
|
|
|
|
| 864 |
document.getElementById('loadingOverlay').style.display = 'none';
|
| 865 |
});
|
| 866 |
}
|
| 867 |
+
|
| 868 |
+
function manualUpdateOrder(productId, val) {
|
| 869 |
+
let num = parseInt(val);
|
| 870 |
+
if (isNaN(num) || num < 0) return;
|
| 871 |
+
document.getElementById('loadingOverlay').style.display = 'flex';
|
| 872 |
+
fetch(`/edit_order/{{ order.id }}`, {
|
| 873 |
+
method: 'POST',
|
| 874 |
+
headers: { 'Content-Type': 'application/json' },
|
| 875 |
+
body: JSON.stringify({ product_id: productId, exact_qty: num })
|
| 876 |
+
})
|
| 877 |
+
.then(r => r.json())
|
| 878 |
+
.then(data => {
|
| 879 |
+
if(data.success) {
|
| 880 |
+
window.location.reload();
|
| 881 |
+
} else {
|
| 882 |
+
alert('Ошибка обновления');
|
| 883 |
+
document.getElementById('loadingOverlay').style.display = 'none';
|
| 884 |
+
}
|
| 885 |
+
})
|
| 886 |
+
.catch(() => {
|
| 887 |
+
alert('Произошла ошибка');
|
| 888 |
+
document.getElementById('loadingOverlay').style.display = 'none';
|
| 889 |
+
});
|
| 890 |
+
}
|
| 891 |
</script>
|
| 892 |
</body>
|
| 893 |
</html>
|
|
|
|
| 1247 |
req_data = request.get_json()
|
| 1248 |
product_id = req_data.get('product_id')
|
| 1249 |
change = req_data.get('change', 0)
|
| 1250 |
+
exact_qty = req_data.get('exact_qty')
|
| 1251 |
remove = req_data.get('remove', False)
|
| 1252 |
|
| 1253 |
for item in order['cart']:
|
|
|
|
| 1255 |
if remove:
|
| 1256 |
order['cart'].remove(item)
|
| 1257 |
else:
|
| 1258 |
+
if exact_qty is not None:
|
| 1259 |
+
item['quantity'] = int(exact_qty)
|
| 1260 |
+
else:
|
| 1261 |
+
item['quantity'] += change
|
| 1262 |
+
|
| 1263 |
if item['quantity'] <= 0:
|
| 1264 |
order['cart'].remove(item)
|
| 1265 |
break
|
|
|
|
| 1423 |
threading.Thread(target=periodic_backup, daemon=True).start()
|
| 1424 |
|
| 1425 |
port = int(os.environ.get('PORT', 7860))
|
| 1426 |
+
app.run(host='0.0.0.0', port=port)
|