fix: DockerSecretsResolver - don't normalize absolute paths like /var/www/html/...
Some checks failed
Deploy Application / deploy (push) Has been cancelled

This commit is contained in:
2025-11-24 21:28:25 +01:00
parent 4eb7134853
commit 77abc65cd7
1327 changed files with 91915 additions and 9909 deletions

View File

@@ -0,0 +1,190 @@
/**
* Asset Preview Module
*
* Handles asset preview thumbnails, lightbox, and detail views
*/
export class AssetPreview {
constructor() {
this.lightbox = null;
this.init();
}
init() {
this.setupThumbnailClicks();
this.setupLightbox();
this.setupCopyUrl();
this.setupVariantDeletion();
}
setupThumbnailClicks() {
document.addEventListener('click', (e) => {
const thumbnail = e.target.closest('.asset-preview-thumbnail');
if (!thumbnail) return;
const assetId = thumbnail.dataset.assetId;
if (!assetId) return;
// Navigate to detail view
window.location.href = `/admin/assets/${assetId}`;
});
}
setupLightbox() {
// Create lightbox HTML
const lightboxHTML = `
<div class="asset-lightbox" id="asset-lightbox" style="display: none;">
<div class="asset-lightbox__content">
<img class="asset-lightbox__image" src="" alt="Asset Preview" />
<button class="asset-lightbox__close" data-close-lightbox>×</button>
<div class="asset-lightbox__info">
<p class="asset-lightbox__filename"></p>
<p class="asset-lightbox__size"></p>
</div>
</div>
</div>
`;
// Add lightbox to body if not exists
if (!document.getElementById('asset-lightbox')) {
document.body.insertAdjacentHTML('beforeend', lightboxHTML);
this.lightbox = document.getElementById('asset-lightbox');
} else {
this.lightbox = document.getElementById('asset-lightbox');
}
// Setup image clicks for lightbox
document.addEventListener('click', (e) => {
const img = e.target.closest('img[data-lightbox]');
if (!img) return;
e.preventDefault();
this.openLightbox(img.src, img.alt, img.dataset.filename, img.dataset.size);
});
// Close lightbox
document.addEventListener('click', (e) => {
if (e.target.closest('[data-close-lightbox]') || e.target === this.lightbox) {
this.closeLightbox();
}
});
// Close on Escape key
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && this.lightbox && this.lightbox.style.display !== 'none') {
this.closeLightbox();
}
});
}
openLightbox(imageUrl, alt, filename, size) {
if (!this.lightbox) return;
const img = this.lightbox.querySelector('.asset-lightbox__image');
const filenameEl = this.lightbox.querySelector('.asset-lightbox__filename');
const sizeEl = this.lightbox.querySelector('.asset-lightbox__size');
img.src = imageUrl;
img.alt = alt;
if (filenameEl) filenameEl.textContent = filename || alt;
if (sizeEl) sizeEl.textContent = size || '';
this.lightbox.style.display = 'flex';
document.body.style.overflow = 'hidden';
}
closeLightbox() {
if (!this.lightbox) return;
this.lightbox.style.display = 'none';
document.body.style.overflow = '';
}
setupCopyUrl() {
document.addEventListener('click', async (e) => {
const copyButton = e.target.closest('[data-copy-url]');
if (!copyButton) return;
e.preventDefault();
try {
const url = copyButton.href;
const response = await fetch(url);
const data = await response.json();
const assetUrl = data.url || url;
await navigator.clipboard.writeText(assetUrl);
// Show success feedback
const originalText = copyButton.textContent;
copyButton.textContent = 'Copied!';
copyButton.classList.add('copied');
setTimeout(() => {
copyButton.textContent = originalText;
copyButton.classList.remove('copied');
}, 2000);
} catch (error) {
console.error('Failed to copy URL:', error);
alert('Failed to copy URL');
}
});
}
setupVariantDeletion() {
document.addEventListener('click', async (e) => {
const deleteButton = e.target.closest('[data-delete-variant]');
if (!deleteButton) return;
e.preventDefault();
const variant = deleteButton.dataset.variant;
const assetId = deleteButton.closest('[data-asset-id]')?.dataset.assetId ||
window.location.pathname.split('/').pop();
if (!confirm(`Are you sure you want to delete variant "${variant}"?`)) {
return;
}
try {
deleteButton.disabled = true;
deleteButton.textContent = 'Deleting...';
const response = await fetch(`/api/v1/assets/${assetId}/variants/${variant}`, {
method: 'DELETE',
headers: {
'X-Requested-With': 'XMLHttpRequest',
},
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
// Remove variant item from DOM
deleteButton.closest('.variant-item')?.remove();
// Show success message
if (window.Toast) {
window.Toast.success(`Variant "${variant}" deleted successfully`);
}
} catch (error) {
console.error('Failed to delete variant:', error);
deleteButton.disabled = false;
deleteButton.textContent = 'Delete';
if (window.Toast) {
window.Toast.error(`Failed to delete variant: ${error.message}`);
} else {
alert(`Failed to delete variant: ${error.message}`);
}
}
});
}
}
// Auto-initialize
document.addEventListener('DOMContentLoaded', () => {
new AssetPreview();
});