picture qol fixes
This commit is contained in:
22
app.py
22
app.py
@@ -245,6 +245,28 @@ def bulk_delete():
|
|||||||
print(f"Bulk delete failed: {e}")
|
print(f"Bulk delete failed: {e}")
|
||||||
return jsonify({"error": str(e)}), 500
|
return jsonify({"error": str(e)}), 500
|
||||||
|
|
||||||
|
@app.route('/upload_image', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def upload_image():
|
||||||
|
if 'image' not in request.files or 'barcode' not in request.form:
|
||||||
|
return jsonify({"error": "Missing data"}), 400
|
||||||
|
|
||||||
|
file = request.files['image']
|
||||||
|
barcode = request.form['barcode']
|
||||||
|
|
||||||
|
if file.filename == '' or not barcode:
|
||||||
|
return jsonify({"error": "Invalid data"}), 400
|
||||||
|
|
||||||
|
# Detect extension
|
||||||
|
ext = mimetypes.guess_extension(file.mimetype) or '.jpg'
|
||||||
|
filename = f"{barcode}{ext}"
|
||||||
|
filepath = os.path.join(CACHE_DIR, filename)
|
||||||
|
|
||||||
|
file.save(filepath)
|
||||||
|
|
||||||
|
# Return the relative path for the frontend
|
||||||
|
return jsonify({"status": "success", "image_url": f"/static/cache/{filename}"}), 200
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
init_db()
|
init_db()
|
||||||
socketio.run(app, host='0.0.0.0', port=5000, debug=True)
|
socketio.run(app, host='0.0.0.0', port=5000, debug=True)
|
||||||
|
|||||||
@@ -121,9 +121,11 @@
|
|||||||
|
|
||||||
/* ── Price tag ── */
|
/* ── Price tag ── */
|
||||||
.price-tag {
|
.price-tag {
|
||||||
font-size: 2.2rem;
|
font-size: 2.8rem;
|
||||||
|
/* Slightly larger for the wider card */
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
color: var(--text-main);
|
color: var(--accent);
|
||||||
|
/* Optional: uses your accent color for better visibility */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Table ── */
|
/* ── Table ── */
|
||||||
@@ -185,8 +187,13 @@
|
|||||||
|
|
||||||
/* ── Product image ── */
|
/* ── Product image ── */
|
||||||
#display-img {
|
#display-img {
|
||||||
max-width: 160px;
|
width: 100%;
|
||||||
max-height: 160px;
|
/* Allows it to fill the new width */
|
||||||
|
max-width: 250px;
|
||||||
|
/* Increased from 160px */
|
||||||
|
height: auto;
|
||||||
|
max-height: 250px;
|
||||||
|
/* Increased from 160px */
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,7 +286,7 @@
|
|||||||
<div class="row g-3">
|
<div class="row g-3">
|
||||||
|
|
||||||
<!-- ── LEFT COLUMN ── -->
|
<!-- ── LEFT COLUMN ── -->
|
||||||
<div class="col-12 col-lg-4">
|
<div class="col-12 col-lg-5">
|
||||||
|
|
||||||
<!-- New product prompt -->
|
<!-- New product prompt -->
|
||||||
<div id="new-product-prompt"
|
<div id="new-product-prompt"
|
||||||
@@ -313,17 +320,32 @@
|
|||||||
required>
|
required>
|
||||||
<input class="form-control mb-2" type="number" name="price" id="form-price"
|
<input class="form-control mb-2" type="number" name="price" id="form-price"
|
||||||
placeholder="Precio (CLP)" required>
|
placeholder="Precio (CLP)" required>
|
||||||
<input class="form-control mb-3" type="text" name="image_url" id="form-image"
|
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<input class="form-control" type="text" name="image_url" id="form-image"
|
||||||
placeholder="URL Imagen">
|
placeholder="URL Imagen">
|
||||||
<button type="submit" class="btn btn-accent w-100">
|
<input type="file" id="camera-input" accept="image/*" capture="environment"
|
||||||
<i class="bi bi-floppy me-1"></i>Guardar Cambios
|
style="display: none;" onchange="handleFileUpload(this)">
|
||||||
|
<button class="btn btn-outline-secondary" type="button"
|
||||||
|
onclick="document.getElementById('camera-input').click()">
|
||||||
|
<i class="bi bi-camera"></i>
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<button type="submit" class="btn btn-accent flex-grow-1">
|
||||||
|
<i class="bi bi-floppy me-1"></i>Guardar
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-secondary" onclick="clearForm()">
|
||||||
|
<i class="bi bi-eraser"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ── RIGHT COLUMN ── -->
|
<!-- ── RIGHT COLUMN ── -->
|
||||||
<div class="col-12 col-lg-8">
|
<div class="col-12 col-lg-7">
|
||||||
<div class="discord-card p-3">
|
<div class="discord-card p-3">
|
||||||
|
|
||||||
<!-- Bulk actions bar -->
|
<!-- Bulk actions bar -->
|
||||||
@@ -559,10 +581,15 @@
|
|||||||
dismissPrompt();
|
dismissPrompt();
|
||||||
document.getElementById('form-barcode').value = b;
|
document.getElementById('form-barcode').value = b;
|
||||||
document.getElementById('form-name').value = n;
|
document.getElementById('form-name').value = n;
|
||||||
// If p is undefined (from scan_error), set value to empty string
|
|
||||||
document.getElementById('form-price').value = (p !== undefined && p !== null) ? p : '';
|
document.getElementById('form-price').value = (p !== undefined && p !== null) ? p : '';
|
||||||
document.getElementById('form-image').value = i || '';
|
document.getElementById('form-image').value = i || '';
|
||||||
document.getElementById('form-title').innerText = t;
|
document.getElementById('form-title').innerText = t;
|
||||||
|
|
||||||
|
// ADD THESE LINES TO UPDATE THE PREVIEW CARD
|
||||||
|
document.getElementById('display-img').src = i || './static/placeholder.png';
|
||||||
|
document.getElementById('display-name').innerText = n || 'Producto Nuevo';
|
||||||
|
document.getElementById('display-price').innerText = clp.format(p || 0);
|
||||||
|
document.getElementById('display-barcode').innerText = b;
|
||||||
}
|
}
|
||||||
|
|
||||||
function dismissPrompt() {
|
function dismissPrompt() {
|
||||||
@@ -574,7 +601,7 @@
|
|||||||
document.getElementById('editModal').dataset.barcode = b;
|
document.getElementById('editModal').dataset.barcode = b;
|
||||||
document.getElementById('editModal').dataset.name = n;
|
document.getElementById('editModal').dataset.name = n;
|
||||||
document.getElementById('editModal').dataset.price = p;
|
document.getElementById('editModal').dataset.price = p;
|
||||||
document.getElementById('editModal').dataset.image = i;
|
document.getElementById('editModal').dataset.image = i; // This captures the image
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmEdit() {
|
function confirmEdit() {
|
||||||
@@ -621,10 +648,49 @@
|
|||||||
document.getElementById('selected-count').innerText =
|
document.getElementById('selected-count').innerText =
|
||||||
document.querySelectorAll('.product-checkbox:checked').length;
|
document.querySelectorAll('.product-checkbox:checked').length;
|
||||||
}
|
}
|
||||||
function clearSelection() {
|
|
||||||
document.querySelectorAll('.product-checkbox').forEach(cb => cb.checked = false);
|
function clearForm() {
|
||||||
document.getElementById('select-all').checked = false;
|
document.getElementById('product-form').reset();
|
||||||
updateBulkBar();
|
document.getElementById('form-title').innerText = 'Editar / Crear';
|
||||||
|
// Reset preview card
|
||||||
|
document.getElementById('display-img').src = './static/placeholder.png';
|
||||||
|
document.getElementById('display-name').innerText = 'Esperando scan...';
|
||||||
|
document.getElementById('display-price').innerText = '$0';
|
||||||
|
document.getElementById('display-barcode').innerText = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleFileUpload(input) {
|
||||||
|
const barcode = document.getElementById('form-barcode').value;
|
||||||
|
if (!barcode) {
|
||||||
|
alert("Primero escanea o ingresa un código de barras.");
|
||||||
|
input.value = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const file = input.files[0];
|
||||||
|
if (!file) return;
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('image', file);
|
||||||
|
formData.append('barcode', barcode);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch('/upload_image', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
|
if (res.ok) {
|
||||||
|
document.getElementById('form-image').value = data.image_url;
|
||||||
|
document.getElementById('display-img').src = data.image_url;
|
||||||
|
} else {
|
||||||
|
alert("Error al subir imagen: " + data.error);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
alert("Error de conexión al subir imagen.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyBulkPrice() {
|
function applyBulkPrice() {
|
||||||
|
|||||||
Reference in New Issue
Block a user