mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-05-24 07:10:21 -05:00
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:
@@ -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 %}
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
Reference in New Issue
Block a user