Kgshop commited on
Commit
ca44ea6
·
verified ·
1 Parent(s): 84ffafd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +51 -38
app.py CHANGED
@@ -800,6 +800,8 @@ ADMIN_TEMPLATE = """
800
  .btn { padding: 12px 20px; font-size: 1em; border: none; border-radius: 8px; font-weight: 600; cursor: pointer; transition: background-color 0.2s ease; }
801
  .btn-primary { background-color: var(--admin-primary); color: #000; }
802
  .btn-primary:hover { background-color: var(--admin-primary-dark); }
 
 
803
  .btn-delete { background-color: var(--admin-danger); color: white; }
804
  .btn-delete:hover { background-color: #c82333; }
805
  .user-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: var(--padding); margin-top: var(--padding); }
@@ -809,11 +811,7 @@ ADMIN_TEMPLATE = """
809
  .user-info img { width: 60px; height: 60px; border-radius: 50%; object-fit: cover; border: 3px solid var(--admin-border); background-color: #eee; }
810
  .user-details .name { font-weight: 600; font-size: 1.2em; }
811
  .user-details .username { color: var(--admin-secondary); font-size: 0.9em; }
812
- .referral-info {
813
- font-size: 0.85em; color: var(--admin-secondary); margin-top: 8px;
814
- padding: 8px; background-color: #f8f9fa; border-radius: 8px;
815
- border: 1px solid var(--admin-border);
816
- }
817
  .referral-info .ref-by { color: var(--admin-success); }
818
  .referral-info .ref-count { color: var(--admin-info); font-weight: 600; }
819
  .user-balances { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; text-align: center; margin-bottom: 1rem; margin-top: 1rem; }
@@ -838,9 +836,6 @@ ADMIN_TEMPLATE = """
838
  .form-group { display: flex; flex-direction: column; }
839
  .form-group label { margin-bottom: 0.5rem; font-weight: 500; font-size: 0.9em; }
840
  .form-group input, .form-group textarea { padding: 10px; font-size: 1rem; border: 1px solid var(--admin-border); border-radius: 8px; width: 100%; box-sizing: border-box; }
841
- .calculation-summary { background: #f0f0f0; padding: 1rem; border-radius: 8px; margin-top: 1rem; }
842
- .summary-item { display: flex; justify-content: space-between; margin-bottom: 0.5rem; font-size: 0.95em; }
843
- .summary-item strong { font-weight: 600; }
844
  .history-container { margin-top: 1.5rem; }
845
  .history-container h3 { font-size: 1.2rem; margin-bottom: 1rem; }
846
  .history-list { list-style: none; padding: 0; max-height: 200px; overflow-y: auto; border: 1px solid var(--admin-border); border-radius: 8px; }
@@ -863,11 +858,16 @@ ADMIN_TEMPLATE = """
863
  .tab-btn.active { color: var(--admin-primary-dark); border-bottom-color: var(--admin-primary); }
864
  .tab-content { display: none; }
865
  .tab-content.active { display: block; }
866
- .invoice-items-table { width: 100%; border-collapse: collapse; margin-top: 1rem; }
867
- .invoice-items-table th, .invoice-items-table td { border: 1px solid var(--admin-border); padding: 8px; text-align: left; font-size: 0.9em; }
868
- .invoice-items-table th { background-color: #e9ecef; font-weight: 600; color: var(--admin-text); }
869
- .invoice-items-table .total-row td { font-weight: 700; background-color: #f0f0f0; }
870
- .invoice-items-table .action-btn { background: none; border: none; color: var(--admin-danger); cursor: pointer; font-size: 1.2em; }
 
 
 
 
 
871
  .invoice-section-summary { padding: 1rem; background-color: #e9ecef; border-radius: 8px; margin-top: 1rem; font-weight: 600; font-size: 1.1em; text-align: right; }
872
  .invoice-section-summary span { color: var(--admin-success); }
873
  .invoice-list-admin { list-style: none; padding: 0; max-height: 200px; overflow-y: auto; border: 1px solid var(--admin-border); border-radius: 8px; }
@@ -890,7 +890,7 @@ ADMIN_TEMPLATE = """
890
  .item-total { flex-basis: 20%; text-align: right; font-weight: bold; }
891
  .invoice-total-display { margin-top: 1rem; padding-top: 1rem; border-top: 1px solid #dee2e6; display: flex; flex-direction: column; gap: 0.5rem; font-size: 1.1em; }
892
  .invoice-total-display div { display: flex; justify-content: space-between; }
893
- .bonus-source-selector { display: flex; gap: 1rem; margin-bottom: 1rem; align-items: center; }
894
  .bonus-source-selector label { display: flex; align-items: center; gap: 0.5rem; cursor: pointer; }
895
  </style>
896
  </head>
@@ -1026,22 +1026,14 @@ ADMIN_TEMPLATE = """
1026
  <div id="invoice-tab" class="tab-content">
1027
  <div class="form-section">
1028
  <h3>Добавить новую накладную</h3>
1029
- <table class="invoice-items-table" id="newInvoiceItemsTable">
1030
- <thead>
1031
- <tr>
1032
- <th>Товар</th><th>Кол-во</th><th>Цена за ед.</th>
1033
- <th>Сумма</th><th></th>
1034
- </tr>
1035
- </thead>
1036
- <tbody></tbody>
1037
- <tfoot>
1038
- <tr class="total-row">
1039
- <td colspan="3" style="text-align: right;"><strong>Итоговая сумма:</strong></td>
1040
- <td id="newInvoiceTotalAmount">0.00</td><td></td>
1041
- </tr>
1042
- </tfoot>
1043
- </table>
1044
- <button class="btn btn-primary" style="margin-top: 1rem;" onclick="addNewInvoiceItemRow()">Добавить товар</button>
1045
  </div>
1046
  <div class="form-section">
1047
  <h3>Оплата бонусами</h3>
@@ -1454,9 +1446,10 @@ ADMIN_TEMPLATE = """
1454
  const price = parseFloat(newInvoiceItems[index].unit_price) || 0;
1455
  const itemTotal = qty * price;
1456
  newInvoiceItems[index].item_total = itemTotal;
1457
- const tableBody = document.getElementById('newInvoiceItemsTable').getElementsByTagName('tbody')[0];
1458
- if (tableBody.rows[index]) {
1459
- tableBody.rows[index].querySelector('.item-total-display').textContent = itemTotal.toFixed(2);
 
1460
  }
1461
  updateNewInvoiceTotal();
1462
  }
@@ -1467,12 +1460,32 @@ ADMIN_TEMPLATE = """
1467
  }
1468
 
1469
  function renderNewInvoiceItems() {
1470
- const tableBody = document.getElementById('newInvoiceItemsTable').getElementsByTagName('tbody')[0];
1471
- tableBody.innerHTML = '';
1472
  newInvoiceItems.forEach((item, index) => {
1473
- const newRow = tableBody.insertRow();
 
1474
  const productName = item.product_name ? String(item.product_name).replace(/"/g, '&quot;') : '';
1475
- newRow.innerHTML = `<td><input type="text" placeholder="Название товара" value="${productName}" oninput="updateInvoiceItem(${index}, 'product_name', this.value)"></td><td><input type="number" step="1" min="1" placeholder="1" value="${item.quantity || '1'}" oninput="updateInvoiceItem(${index}, 'quantity', this.value)"></td><td><input type="number" step="0.01" min="0" placeholder="0.00" value="${item.unit_price || ''}" oninput="updateInvoiceItem(${index}, 'unit_price', this.value)"></td><td class="item-total-display">${(item.item_total || 0).toFixed(2)}</td><td><button class="action-btn" onclick="removeInvoiceItemRow(${index})">🗑️</button></td>`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1476
  });
1477
  updateNewInvoiceTotal();
1478
  }
@@ -2030,4 +2043,4 @@ if __name__ == '__main__':
2030
  print("WARNING: HF_TOKEN_WRITE not set. Periodic backups to Hugging Face are disabled.")
2031
 
2032
  print("--- Server Ready ---")
2033
- app.run(host=HOST, port=PORT, debug=False)
 
800
  .btn { padding: 12px 20px; font-size: 1em; border: none; border-radius: 8px; font-weight: 600; cursor: pointer; transition: background-color 0.2s ease; }
801
  .btn-primary { background-color: var(--admin-primary); color: #000; }
802
  .btn-primary:hover { background-color: var(--admin-primary-dark); }
803
+ .btn-secondary { background-color: var(--admin-secondary); color: white; }
804
+ .btn-secondary:hover { background-color: #5a6268; }
805
  .btn-delete { background-color: var(--admin-danger); color: white; }
806
  .btn-delete:hover { background-color: #c82333; }
807
  .user-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: var(--padding); margin-top: var(--padding); }
 
811
  .user-info img { width: 60px; height: 60px; border-radius: 50%; object-fit: cover; border: 3px solid var(--admin-border); background-color: #eee; }
812
  .user-details .name { font-weight: 600; font-size: 1.2em; }
813
  .user-details .username { color: var(--admin-secondary); font-size: 0.9em; }
814
+ .referral-info { font-size: 0.85em; color: var(--admin-secondary); margin-top: 8px; padding: 8px; background-color: #f8f9fa; border-radius: 8px; border: 1px solid var(--admin-border); }
 
 
 
 
815
  .referral-info .ref-by { color: var(--admin-success); }
816
  .referral-info .ref-count { color: var(--admin-info); font-weight: 600; }
817
  .user-balances { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; text-align: center; margin-bottom: 1rem; margin-top: 1rem; }
 
836
  .form-group { display: flex; flex-direction: column; }
837
  .form-group label { margin-bottom: 0.5rem; font-weight: 500; font-size: 0.9em; }
838
  .form-group input, .form-group textarea { padding: 10px; font-size: 1rem; border: 1px solid var(--admin-border); border-radius: 8px; width: 100%; box-sizing: border-box; }
 
 
 
839
  .history-container { margin-top: 1.5rem; }
840
  .history-container h3 { font-size: 1.2rem; margin-bottom: 1rem; }
841
  .history-list { list-style: none; padding: 0; max-height: 200px; overflow-y: auto; border: 1px solid var(--admin-border); border-radius: 8px; }
 
858
  .tab-btn.active { color: var(--admin-primary-dark); border-bottom-color: var(--admin-primary); }
859
  .tab-content { display: none; }
860
  .tab-content.active { display: block; }
861
+ #newInvoiceItemsList { display: flex; flex-direction: column; gap: 1rem; margin-bottom: 1rem; }
862
+ .invoice-item-row { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end; padding-bottom: 10px; border-bottom: 1px solid var(--admin-border); }
863
+ .invoice-item-row .form-group { margin-bottom: 0; }
864
+ .form-group.item-name { flex: 3 1 200px; }
865
+ .form-group.item-qty, .form-group.item-price { flex: 1 1 80px; }
866
+ .item-total-group { flex: 1 1 90px; text-align: right; padding-bottom: 10px; }
867
+ .item-total-group .item-total-display { font-weight: 700; font-size: 1.1em; }
868
+ .invoice-item-row .action-btn { align-self: center; margin-bottom: 5px; background: none; border: none; color: var(--admin-danger); cursor: pointer; font-size: 1.2em; }
869
+ .invoice-items-footer { display: flex; justify-content: space-between; align-items: center; margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--admin-border); }
870
+ .invoice-items-total { font-size: 1.2em; font-weight: 700; }
871
  .invoice-section-summary { padding: 1rem; background-color: #e9ecef; border-radius: 8px; margin-top: 1rem; font-weight: 600; font-size: 1.1em; text-align: right; }
872
  .invoice-section-summary span { color: var(--admin-success); }
873
  .invoice-list-admin { list-style: none; padding: 0; max-height: 200px; overflow-y: auto; border: 1px solid var(--admin-border); border-radius: 8px; }
 
890
  .item-total { flex-basis: 20%; text-align: right; font-weight: bold; }
891
  .invoice-total-display { margin-top: 1rem; padding-top: 1rem; border-top: 1px solid #dee2e6; display: flex; flex-direction: column; gap: 0.5rem; font-size: 1.1em; }
892
  .invoice-total-display div { display: flex; justify-content: space-between; }
893
+ .bonus-source-selector { display: flex; gap: 1rem; margin-bottom: 1rem; align-items: center; flex-wrap: wrap; }
894
  .bonus-source-selector label { display: flex; align-items: center; gap: 0.5rem; cursor: pointer; }
895
  </style>
896
  </head>
 
1026
  <div id="invoice-tab" class="tab-content">
1027
  <div class="form-section">
1028
  <h3>Добавить новую накладную</h3>
1029
+ <div id="newInvoiceItemsList"></div>
1030
+ <div class="invoice-items-footer">
1031
+ <button class="btn btn-secondary" onclick="addNewInvoiceItemRow()">Добавить товар</button>
1032
+ <div class="invoice-items-total">
1033
+ <strong>Итого:</strong>
1034
+ <span id="newInvoiceTotalAmount">0.00</span>
1035
+ </div>
1036
+ </div>
 
 
 
 
 
 
 
 
1037
  </div>
1038
  <div class="form-section">
1039
  <h3>Оплата бонусами</h3>
 
1446
  const price = parseFloat(newInvoiceItems[index].unit_price) || 0;
1447
  const itemTotal = qty * price;
1448
  newInvoiceItems[index].item_total = itemTotal;
1449
+ const list = document.getElementById('newInvoiceItemsList');
1450
+ const itemRow = list.children[index];
1451
+ if (itemRow) {
1452
+ itemRow.querySelector('.item-total-display').textContent = itemTotal.toFixed(2);
1453
  }
1454
  updateNewInvoiceTotal();
1455
  }
 
1460
  }
1461
 
1462
  function renderNewInvoiceItems() {
1463
+ const list = document.getElementById('newInvoiceItemsList');
1464
+ list.innerHTML = '';
1465
  newInvoiceItems.forEach((item, index) => {
1466
+ const itemDiv = document.createElement('div');
1467
+ itemDiv.className = 'invoice-item-row';
1468
  const productName = item.product_name ? String(item.product_name).replace(/"/g, '&quot;') : '';
1469
+ itemDiv.innerHTML = `
1470
+ <div class="form-group item-name">
1471
+ <label>Товар</label>
1472
+ <input type="text" placeholder="Название товара" value="${productName}" oninput="updateInvoiceItem(${index}, 'product_name', this.value)">
1473
+ </div>
1474
+ <div class="form-group item-qty">
1475
+ <label>Кол-во</label>
1476
+ <input type="number" step="1" min="1" placeholder="1" value="${item.quantity || '1'}" oninput="updateInvoiceItem(${index}, 'quantity', this.value)">
1477
+ </div>
1478
+ <div class="form-group item-price">
1479
+ <label>Цена</label>
1480
+ <input type="number" step="0.01" min="0" placeholder="0.00" value="${item.unit_price || ''}" oninput="updateInvoiceItem(${index}, 'unit_price', this.value)">
1481
+ </div>
1482
+ <div class="item-total-group">
1483
+ <label>Сумма</label>
1484
+ <div class="item-total-display">${(item.item_total || 0).toFixed(2)}</div>
1485
+ </div>
1486
+ <button class="action-btn" onclick="removeInvoiceItemRow(${index})">🗑️</button>
1487
+ `;
1488
+ list.appendChild(itemDiv);
1489
  });
1490
  updateNewInvoiceTotal();
1491
  }
 
2043
  print("WARNING: HF_TOKEN_WRITE not set. Periodic backups to Hugging Face are disabled.")
2044
 
2045
  print("--- Server Ready ---")
2046
+ app.run(host=HOST, port=PORT, debug=False)