ui: remove decorative image upload from invoice and quote edit forms

Decorative images are only managed via Admin PDF layout templates. Remove the per-document upload section and related JS from invoice and quote edit pages so users do not add images there; template-based decorative images in pdf_layout/quote_pdf_layout remain unchanged.
This commit is contained in:
Dries Peeters
2026-03-01 07:36:31 +01:00
parent 552675ff55
commit 36d64e0cb1
2 changed files with 0 additions and 279 deletions
-139
View File
@@ -56,55 +56,6 @@
</div>
</div>
<!-- Decorative Images Section -->
<div class="mt-8 border-t border-border-light dark:border-border-dark pt-6">
<div class="flex items-center justify-between mb-4">
<div>
<h2 class="text-lg font-semibold flex items-center">
<i class="fas fa-image mr-2 text-purple-600"></i>
{{ _('Decorative Images') }}
</h2>
<p class="text-sm text-text-muted-light dark:text-text-muted-dark mt-1">{{ _('Add decorative images to appear in the PDF') }}</p>
</div>
<button type="button" id="upload-image-btn" class="bg-purple-500 text-white px-4 py-2 rounded-lg hover:bg-purple-600 transition shadow-sm">
<i class="fas fa-upload mr-2"></i>{{ _('Upload Image') }}
</button>
</div>
<input type="file" id="image-upload-input" accept="image/png,image/jpeg,image/jpg,image/gif,image/webp" style="display: none;">
<div id="images-gallery" class="grid grid-cols-2 md:grid-cols-4 gap-4">
{% if invoice.decorative_images %}
{% for image in invoice.decorative_images %}
<div class="relative group border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden bg-gray-50 dark:bg-gray-800" data-image-id="{{ image.id }}">
<img src="{{ url_for('invoices.get_invoice_image_base64', invoice_id=invoice.id, image_id=image.id) }}"
alt="{{ image.original_filename }}"
class="w-full h-32 object-cover">
<div class="absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-50 transition-opacity flex items-center justify-center">
<button type="button"
class="delete-image-btn opacity-0 group-hover:opacity-100 bg-red-500 text-white px-3 py-1 rounded hover:bg-red-600 transition"
data-image-id="{{ image.id }}"
data-invoice-id="{{ invoice.id }}">
<i class="fas fa-trash mr-1"></i>{{ _('Delete') }}
</button>
</div>
<div class="p-2 text-xs text-gray-600 dark:text-gray-400 truncate" title="{{ image.original_filename }}">
{{ image.original_filename }}
</div>
</div>
{% endfor %}
{% else %}
<div class="col-span-full text-center py-8 text-text-muted-light dark:text-text-muted-dark">
<i class="fas fa-image text-4xl mb-2 opacity-50"></i>
<p>{{ _('No images uploaded yet') }}</p>
</div>
{% endif %}
</div>
<p class="text-xs text-text-muted-light dark:text-text-muted-dark mt-2">
<i class="fas fa-info-circle mr-1"></i>{{ _('Position images in the PDF Designer after uploading') }}
</p>
</div>
<div class="mt-8 border-t border-border-light dark:border-border-dark pt-6">
<div class="flex items-center justify-between mb-4">
<div>
@@ -893,95 +844,5 @@ function sendInvoiceEmail(event) {
submitBtn.textContent = originalText;
});
}
// Image upload handling
document.addEventListener('DOMContentLoaded', function() {
const uploadBtn = document.getElementById('upload-image-btn');
const uploadInput = document.getElementById('image-upload-input');
const imagesGallery = document.getElementById('images-gallery');
if (uploadBtn && uploadInput) {
uploadBtn.addEventListener('click', () => uploadInput.click());
uploadInput.addEventListener('change', function(e) {
const file = e.target.files[0];
if (!file) return;
// Validate file type
const allowedTypes = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/webp'];
if (!allowedTypes.includes(file.type)) {
alert('{{ _("Only image files (PNG, JPG, JPEG, GIF, WEBP) are allowed") }}');
return;
}
// Validate file size (5MB)
if (file.size > 5 * 1024 * 1024) {
alert('{{ _("File size must be less than 5MB") }}');
return;
}
const formData = new FormData();
formData.append('file', file);
formData.append('csrf_token', '{{ csrf_token() }}');
uploadBtn.disabled = true;
uploadBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>{{ _("Uploading...") }}';
fetch('{{ url_for("invoices.upload_invoice_image", invoice_id=invoice.id) }}', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
} else {
alert(data.error || '{{ _("Failed to upload image") }}');
}
})
.catch(error => {
console.error('Upload error:', error);
alert('{{ _("Failed to upload image") }}');
})
.finally(() => {
uploadBtn.disabled = false;
uploadBtn.innerHTML = '<i class="fas fa-upload mr-2"></i>{{ _("Upload Image") }}';
uploadInput.value = '';
});
});
}
// Delete image handling
document.querySelectorAll('.delete-image-btn').forEach(btn => {
btn.addEventListener('click', function() {
const imageId = this.dataset.imageId;
const invoiceId = this.dataset.invoiceId;
if (!confirm('{{ _("Are you sure you want to delete this image?") }}')) {
return;
}
const formData = new FormData();
formData.append('csrf_token', '{{ csrf_token() }}');
fetch(`/invoices/${invoiceId}/images/${imageId}/delete`, {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
} else {
alert(data.error || '{{ _("Failed to delete image") }}');
}
})
.catch(error => {
console.error('Delete error:', error);
alert('{{ _("Failed to delete image") }}');
});
});
});
});
</script>
{% endblock %}
-140
View File
@@ -48,55 +48,6 @@
</div>
</div>
<!-- Decorative Images Section -->
<div class="mb-8">
<div class="flex items-center justify-between mb-4 pb-2 border-b border-border-light dark:border-border-dark">
<div>
<h2 class="text-xl font-semibold flex items-center">
<i class="fas fa-image mr-2 text-purple-600"></i>
{{ _('Decorative Images') }}
</h2>
<p class="text-sm text-text-muted-light dark:text-text-muted-dark mt-1">{{ _('Add decorative images to appear in the PDF') }}</p>
</div>
<button type="button" id="upload-image-btn" class="bg-purple-500 text-white px-4 py-2 rounded-lg hover:bg-purple-600 transition shadow-sm">
<i class="fas fa-upload mr-2"></i>{{ _('Upload Image') }}
</button>
</div>
<input type="file" id="image-upload-input" accept="image/png,image/jpeg,image/jpg,image/gif,image/webp" style="display: none;">
<div id="images-gallery" class="grid grid-cols-2 md:grid-cols-4 gap-4">
{% if quote.decorative_images %}
{% for image in quote.decorative_images %}
<div class="relative group border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden bg-gray-50 dark:bg-gray-800" data-image-id="{{ image.id }}">
<img src="{{ url_for('quotes.get_quote_image_base64', quote_id=quote.id, image_id=image.id) }}"
alt="{{ image.original_filename }}"
class="w-full h-32 object-cover">
<div class="absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-50 transition-opacity flex items-center justify-center">
<button type="button"
class="delete-image-btn opacity-0 group-hover:opacity-100 bg-red-500 text-white px-3 py-1 rounded hover:bg-red-600 transition"
data-image-id="{{ image.id }}"
data-quote-id="{{ quote.id }}">
<i class="fas fa-trash mr-1"></i>{{ _('Delete') }}
</button>
</div>
<div class="p-2 text-xs text-gray-600 dark:text-gray-400 truncate" title="{{ image.original_filename }}">
{{ image.original_filename }}
</div>
</div>
{% endfor %}
{% else %}
<div class="col-span-full text-center py-8 text-text-muted-light dark:text-text-muted-dark">
<i class="fas fa-image text-4xl mb-2 opacity-50"></i>
<p>{{ _('No images uploaded yet') }}</p>
</div>
{% endif %}
</div>
<p class="text-xs text-text-muted-light dark:text-text-muted-dark mt-2">
<i class="fas fa-info-circle mr-1"></i>{{ _('Position images in the PDF Designer after uploading') }}
</p>
</div>
<!-- Quote Items Section -->
<div class="mb-8">
<div class="flex items-center justify-between mb-4 pb-2 border-b border-border-light dark:border-border-dark">
@@ -461,96 +412,5 @@ document.addEventListener('DOMContentLoaded', function() {
// Initial calculation
calculateTotals();
});
});
// Image upload handling
document.addEventListener('DOMContentLoaded', function() {
const uploadBtn = document.getElementById('upload-image-btn');
const uploadInput = document.getElementById('image-upload-input');
const imagesGallery = document.getElementById('images-gallery');
if (uploadBtn && uploadInput) {
uploadBtn.addEventListener('click', () => uploadInput.click());
uploadInput.addEventListener('change', function(e) {
const file = e.target.files[0];
if (!file) return;
// Validate file type
const allowedTypes = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/webp'];
if (!allowedTypes.includes(file.type)) {
alert('{{ _("Only image files (PNG, JPG, JPEG, GIF, WEBP) are allowed") }}');
return;
}
// Validate file size (5MB)
if (file.size > 5 * 1024 * 1024) {
alert('{{ _("File size must be less than 5MB") }}');
return;
}
const formData = new FormData();
formData.append('file', file);
formData.append('csrf_token', '{{ csrf_token() }}');
uploadBtn.disabled = true;
uploadBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>{{ _("Uploading...") }}';
fetch('{{ url_for("quotes.upload_quote_image", quote_id=quote.id) }}', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
} else {
alert(data.error || '{{ _("Failed to upload image") }}');
}
})
.catch(error => {
console.error('Upload error:', error);
alert('{{ _("Failed to upload image") }}');
})
.finally(() => {
uploadBtn.disabled = false;
uploadBtn.innerHTML = '<i class="fas fa-upload mr-2"></i>{{ _("Upload Image") }}';
uploadInput.value = '';
});
});
}
// Delete image handling
document.querySelectorAll('.delete-image-btn').forEach(btn => {
btn.addEventListener('click', function() {
const imageId = this.dataset.imageId;
const quoteId = this.dataset.quoteId;
if (!confirm('{{ _("Are you sure you want to delete this image?") }}')) {
return;
}
const formData = new FormData();
formData.append('csrf_token', '{{ csrf_token() }}');
fetch(`/quotes/${quoteId}/images/${imageId}/delete`, {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
} else {
alert(data.error || '{{ _("Failed to delete image") }}');
}
})
.catch(error => {
console.error('Delete error:', error);
alert('{{ _("Failed to delete image") }}');
});
});
});
});
</script>
{% endblock %}