mirror of
https://github.com/myronblair/tomsjavajive
synced 2026-06-30 17:50:32 -05:00
Rebuild product image upload section — fix broken HTML, wrong textarea name, JS quote errors
This commit is contained in:
+81
-88
@@ -221,109 +221,102 @@ $categories = db()->fetchAll(
|
||||
</div>
|
||||
<div class="admin-card-body">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><div class="form-group">
|
||||
<label class="form-label">Product Images</label>
|
||||
<label class="form-label">Product Images</label>
|
||||
|
||||
<!-- Drag & Drop Upload Zone -->
|
||||
<div id="dropZone" style="border:2px dashed var(--color-border);border-radius:var(--radius-md);padding:2rem;text-align:center;cursor:pointer;transition:all .2s;margin-bottom:1rem;background:var(--color-background)" ondragover="handleDragOver(event)" ondragleave="handleDragLeave(event)" ondrop="handleDrop(event)" onclick="document.getElementById('imageFileInput').click()">
|
||||
<i class="fas fa-cloud-upload-alt" style="font-size:2rem;color:var(--color-text-muted);margin-bottom:.5rem;display:block"></i>
|
||||
<div style="font-weight:600;margin-bottom:.25rem">Drag & drop images here</div>
|
||||
<div style="font-size:.875rem;color:var(--color-text-muted)">or click to browse — JPG, PNG, WebP, GIF up to 5MB each</div>
|
||||
<input type="file" id="imageFileInput" multiple accept="image/*" style="display:none" onchange="handleFileSelect(this.files)">
|
||||
</div>
|
||||
<div id="dropZone" onclick="document.getElementById('imgInput').click()"
|
||||
style="border:2px dashed var(--color-border);border-radius:var(--radius-md);padding:2rem;text-align:center;cursor:pointer;background:var(--color-background);margin-bottom:1rem">
|
||||
<i class="fas fa-cloud-upload-alt" style="font-size:2rem;color:var(--color-text-muted);display:block;margin-bottom:.5rem"></i>
|
||||
<div style="font-weight:600;margin-bottom:.25rem">Drag & drop images here</div>
|
||||
<div style="font-size:.875rem;color:var(--color-text-muted)">or click to browse — JPG, PNG, WebP, GIF up to 5MB each</div>
|
||||
<input type="file" id="imgInput" multiple accept="image/jpeg,image/png,image/gif,image/webp" style="display:none">
|
||||
</div>
|
||||
|
||||
<!-- Upload Preview -->
|
||||
<div id="uploadPreviews" style="display:flex;flex-wrap:wrap;gap:.5rem;margin-bottom:1rem"></div>
|
||||
<div id="imgPreviews" style="display:flex;flex-wrap:wrap;gap:.5rem;margin-bottom:1rem"></div>
|
||||
|
||||
<!-- URL Input -->
|
||||
<div style="position:relative;margin-bottom:.5rem">
|
||||
<div style="display:flex;align-items:center;gap:.5rem;margin-bottom:.5rem;color:var(--color-text-muted);font-size:.875rem">
|
||||
<hr style="flex:1;border:none;border-top:1px solid var(--color-border)">
|
||||
<span>or add image URLs</span>
|
||||
<hr style="flex:1;border:none;border-top:1px solid var(--color-border)">
|
||||
</div>
|
||||
<textarea name="images" id="imageUrls" class="form-textarea" rows="3" placeholder="https://example.com/image1.jpg https://example.com/image2.jpg"><?= htmlspecialchars(is_array($product['images']) ? implode("
|
||||
", $product['images']) : ($product['images'] ?? '')) ?></textarea>
|
||||
<small class="text-muted">Enter one URL per line</small>
|
||||
<textarea name="image_urls" id="imageUrls" class="form-textarea" rows="3"
|
||||
placeholder="https://example.com/image1.jpg"><?= htmlspecialchars(is_array($product['images']) ? implode("\n", $product['images']) : ($product['images'] ?? '')) ?></textarea>
|
||||
<small style="color:var(--color-text-muted)">One URL per line</small>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function handleDragOver(e) {
|
||||
e.preventDefault();
|
||||
document.getElementById('dropZone').style.borderColor = 'var(--color-primary)';
|
||||
document.getElementById('dropZone').style.background = 'rgba(255,94,26,.05)';
|
||||
}
|
||||
function handleDragLeave(e) {
|
||||
document.getElementById('dropZone').style.borderColor = 'var(--color-border)';
|
||||
document.getElementById('dropZone').style.background = 'var(--color-background)';
|
||||
}
|
||||
function handleDrop(e) {
|
||||
e.preventDefault();
|
||||
handleDragLeave(e);
|
||||
handleFileSelect(e.dataTransfer.files);
|
||||
}
|
||||
function handleFileSelect(files) {
|
||||
Array.from(files).forEach(file => {
|
||||
if (!file.type.startsWith('image/')) return;
|
||||
if (file.size > 5 * 1024 * 1024) {
|
||||
alert(file.name + ' is too large (max 5MB)');
|
||||
return;
|
||||
}
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
uploadImage(file, e.target.result);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
}
|
||||
function uploadImage(file, dataUrl) {
|
||||
var preview = document.createElement('div');
|
||||
preview.style.cssText = 'position:relative;width:100px;height:100px;border-radius:var(--radius-md);overflow:hidden;border:1px solid var(--color-border)';
|
||||
preview.innerHTML = '<img src="' + dataUrl + '" style="width:100%;height:100%;object-fit:cover">' +
|
||||
'<div style="position:absolute;inset:0;background:rgba(0,0,0,.4);display:flex;align-items:center;justify-content:center">' +
|
||||
'<i class="fas fa-spinner fa-spin" style="color:#fff;font-size:1.25rem"></i></div>';
|
||||
document.getElementById('uploadPreviews').appendChild(preview);
|
||||
(function() {
|
||||
var zone = document.getElementById('dropZone');
|
||||
var input = document.getElementById('imgInput');
|
||||
|
||||
var formData = new FormData();
|
||||
formData.append('image', file);
|
||||
formData.append('action', 'upload_image');
|
||||
|
||||
fetch('/admin/upload-image.php', {method:'POST', body:formData})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.url) {
|
||||
preview.innerHTML = '<img src="' + data.url + '" style="width:100%;height:100%;object-fit:cover">' +
|
||||
'<button type="button" onclick="removePreview(this, '' + data.url + '')" style="position:absolute;top:2px;right:2px;background:rgba(0,0,0,.6);border:none;color:#fff;border-radius:50%;width:20px;height:20px;cursor:pointer;font-size:.7rem;display:flex;align-items:center;justify-content:center">×</button>';
|
||||
var urls = document.getElementById('imageUrls');
|
||||
urls.value = (urls.value ? urls.value + '
|
||||
' : '') + data.url;
|
||||
} else {
|
||||
preview.innerHTML = '<div style="display:flex;align-items:center;justify-content:center;height:100%;font-size:.75rem;color:var(--color-error);padding:.5rem;text-align:center">' + (data.error || 'Upload failed') + '</div>';
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
preview.innerHTML = '<div style="display:flex;align-items:center;justify-content:center;height:100%;font-size:.75rem;color:var(--color-error);padding:.5rem;text-align:center">Upload failed</div>';
|
||||
zone.addEventListener('dragover', function(e) {
|
||||
e.preventDefault();
|
||||
zone.style.borderColor = 'var(--color-primary)';
|
||||
zone.style.background = 'rgba(255,94,26,.05)';
|
||||
});
|
||||
}
|
||||
function removePreview(btn, url) {
|
||||
btn.closest('div').remove();
|
||||
var urls = document.getElementById('imageUrls');
|
||||
urls.value = urls.value.split('
|
||||
').filter(u => u.trim() !== url.trim()).join('
|
||||
');
|
||||
}
|
||||
zone.addEventListener('dragleave', function() {
|
||||
zone.style.borderColor = 'var(--color-border)';
|
||||
zone.style.background = 'var(--color-background)';
|
||||
});
|
||||
zone.addEventListener('drop', function(e) {
|
||||
e.preventDefault();
|
||||
zone.style.borderColor = 'var(--color-border)';
|
||||
zone.style.background = 'var(--color-background)';
|
||||
processFiles(e.dataTransfer.files);
|
||||
});
|
||||
input.addEventListener('change', function() {
|
||||
processFiles(this.files);
|
||||
this.value = '';
|
||||
});
|
||||
|
||||
function processFiles(files) {
|
||||
Array.from(files).forEach(function(file) {
|
||||
if (!file.type.match(/^image\/(jpeg|png|gif|webp)$/)) {
|
||||
alert(file.name + ': unsupported type');
|
||||
return;
|
||||
}
|
||||
if (file.size > 5 * 1024 * 1024) {
|
||||
alert(file.name + ': exceeds 5MB limit');
|
||||
return;
|
||||
}
|
||||
uploadFile(file);
|
||||
});
|
||||
}
|
||||
|
||||
function uploadFile(file) {
|
||||
var wrap = document.createElement('div');
|
||||
wrap.style.cssText = 'position:relative;width:100px;height:100px;border-radius:6px;overflow:hidden;border:1px solid var(--color-border);background:#111;display:flex;align-items:center;justify-content:center';
|
||||
wrap.innerHTML = '<i class="fas fa-spinner fa-spin" style="color:#fff;font-size:1.25rem"></i>';
|
||||
document.getElementById('imgPreviews').appendChild(wrap);
|
||||
|
||||
var fd = new FormData();
|
||||
fd.append('image', file);
|
||||
|
||||
fetch('/admin/upload-image.php', {method: 'POST', credentials: 'same-origin', body: fd})
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(data) {
|
||||
if (data.url) {
|
||||
wrap.innerHTML =
|
||||
'<img src="' + data.url + '" style="width:100%;height:100%;object-fit:cover">' +
|
||||
'<button type="button" data-url="' + data.url + '" style="position:absolute;top:2px;right:2px;width:20px;height:20px;border-radius:50%;background:rgba(0,0,0,.7);border:none;color:#fff;cursor:pointer;font-size:12px;line-height:1">×</button>';
|
||||
wrap.querySelector('button').addEventListener('click', function() {
|
||||
var url = this.getAttribute('data-url');
|
||||
wrap.remove();
|
||||
var ta = document.getElementById('imageUrls');
|
||||
ta.value = ta.value.split('\n').filter(function(u) { return u.trim() !== url; }).join('\n');
|
||||
});
|
||||
var ta = document.getElementById('imageUrls');
|
||||
ta.value = (ta.value.trim() ? ta.value.trim() + '\n' : '') + data.url;
|
||||
} else {
|
||||
wrap.innerHTML = '<div style="padding:.5rem;font-size:.7rem;color:#f87171;text-align:center">' + (data.error || 'Failed') + '</div>';
|
||||
}
|
||||
})
|
||||
.catch(function() {
|
||||
wrap.innerHTML = '<div style="padding:.5rem;font-size:.7rem;color:#f87171;text-align:center">Upload failed</div>';
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($product['images'])): ?>
|
||||
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap; margin-top: 1rem;">
|
||||
<?php foreach ($product['images'] as $img): ?>
|
||||
<img src="<?= htmlspecialchars($img) ?>" alt="Product image"
|
||||
style="width: 80px; height: 80px; object-fit: cover; border-radius: var(--admin-radius);">
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user