diff --git a/app.py b/app.py index 44bc956..c4ceaf5 100644 --- a/app.py +++ b/app.py @@ -189,6 +189,33 @@ def scan(): def serve_cache(filename): return send_from_directory(CACHE_DIR, filename) +@app.route('/bulk_delete', methods=['POST']) +@login_required +def bulk_delete(): + barcodes = request.json.get('barcodes', []) + with sqlite3.connect(DB_FILE) as conn: + conn.executemany('DELETE FROM products WHERE barcode = ?', [(b,) for b in barcodes]) + conn.commit() + # Optional: Logic to delete cached images could go here + return jsonify({"status": "ok"}) + +@app.route('/bulk_edit_price', methods=['POST']) +@login_required +def bulk_edit_price(): + data = request.json + barcodes = data.get('barcodes', []) + new_price = data.get('price') + + try: + price = float(new_price) + with sqlite3.connect(DB_FILE) as conn: + conn.executemany('UPDATE products SET price = ? WHERE barcode = ?', + [(price, b) for b in barcodes]) + conn.commit() + return jsonify({"status": "ok"}) + except ValueError: + return jsonify({"status": "error", "message": "Invalid price"}), 400 + if __name__ == '__main__': init_db() socketio.run(app, host='0.0.0.0', port=5000, debug=True) diff --git a/static/styleIndex.css b/static/styleIndex.css index 94031bd..97797ac 100644 --- a/static/styleIndex.css +++ b/static/styleIndex.css @@ -179,4 +179,83 @@ th { transform: translateY(0); opacity: 1; } +} + +#bulk-action-bar { + display: none; + /* Controlled by JS */ + background: var(--accent); + padding: 12px 20px; + border-radius: 8px; + margin-bottom: 20px; + color: white; + align-items: center; + gap: 15px; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); + animation: slideDown 0.3s ease; +} + +#bulk-action-bar span { + font-weight: bold; + font-size: 0.9em; + white-space: nowrap; +} + +#bulk-action-bar input[type="number"] { + width: 120px; + margin: 0; + height: 38px; + background: rgba(0, 0, 0, 0.2); + border: 1px solid rgba(255, 255, 255, 0.3); + color: white; +} + +/* Ensure the parent container can hold the absolute bar */ +.column { + position: relative; +} + +#bulk-action-bar { + display: none; + position: absolute; + top: 0; + left: 25px; /* Matches card padding */ + right: 25px; + z-index: 10; + background: var(--accent); + padding: 12px 20px; + border-radius: 8px; + color: white; + align-items: center; + gap: 15px; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3); + animation: slideIn 0.2s ease-out; +} + +@keyframes slideIn { + from { transform: translateY(-10px); opacity: 0; } + to { transform: translateY(0); opacity: 1; } +} + +.btn-bulk-save { + background: #2ecc71; + /* A more professional green */ + color: white; + flex-grow: 1; + font-weight: bold; + height: 38px; +} + +.btn-bulk-save:hover { + background: #27ae60; +} + +.btn-bulk-del { + background: rgba(255, 255, 255, 0.2); + color: white; + height: 38px; +} + +.btn-bulk-del:hover { + background: var(--danger); } \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index def1f81..d3d8bff 100644 --- a/templates/index.html +++ b/templates/index.html @@ -56,11 +56,19 @@
| Código | Nombre | Precio | @@ -70,6 +78,8 @@|
|---|---|---|---|
| {{ p[0] }} | {{ p[1] }} | @@ -194,6 +204,61 @@ tr[i].style.display = content.toUpperCase().indexOf(filter) > -1 ? "" : "none"; } } + + function toggleAll(source) { + document.querySelectorAll('.product-checkbox').forEach(cb => cb.checked = source.checked); + updateBulkBar(); + } + + function updateBulkBar() { + const selected = document.querySelectorAll('.product-checkbox:checked'); + const bar = document.getElementById('bulk-action-bar'); + const countDisplay = document.getElementById('selected-count'); + + if (selected.length > 0) { + bar.style.display = 'flex'; + countDisplay.innerText = `${selected.length} seleccionados`; + } else { + bar.style.display = 'none'; + } + } + + function getSelectedBarcodes() { + return Array.from(document.querySelectorAll('.product-checkbox:checked')).map(cb => cb.value); + } + + async function applyBulkPrice() { + const barcodes = getSelectedBarcodes(); + const price = document.getElementById('bulk-price-input').value; + if (!price) return alert("Ingresa un precio"); + + const resp = await fetch('/bulk_edit_price', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ barcodes, price }) + }); + if (resp.ok) location.reload(); + } + + async function applyBulkDelete() { + if (!confirm("¿Eliminar todos los productos seleccionados?")) return; + const barcodes = getSelectedBarcodes(); + + const resp = await fetch('/bulk_delete', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ barcodes }) + }); + if (resp.ok) location.reload(); + } + + // Clear checkboxes on reload to prevent ghost selections + window.addEventListener('load', () => { + document.querySelectorAll('.product-checkbox').forEach(cb => cb.checked = false); + const selectAll = document.getElementById('select-all'); + if (selectAll) selectAll.checked = false; + updateBulkBar(); + }); |