adding download helper and viewer
Deploy / deploy (push) Successful in 38s

This commit is contained in:
2026-05-12 10:08:17 +02:00
parent 67f31800f3
commit b69e603791
5 changed files with 139 additions and 6 deletions
+47 -5
View File
@@ -588,13 +588,20 @@ async function renderAttachments(eventId, container) {
container.innerHTML = attachments.map(a => {
const icon = getFileIcon(a.mime_type, a.original_name);
const size = formatFileSize(a.file_size);
const ext = a.original_name.split('.').pop().toLowerCase();
const viewable = ['txt', 'md', 'csv'].includes(ext);
const href = viewable ? '#' : '/download/?file=' + encodeURIComponent(a.stored_name) + '&mode=download';
const onclick = viewable ? ` onclick="event.preventDefault();openFileViewer('${esc(a.stored_name)}','${esc(a.original_name)}')"` : '';
return `<div class="attachment-item d-flex align-items-center justify-content-between py-1">
<div class="d-flex align-items-center">
<i class="fas ${icon} me-2" style="font-size:.85rem;"></i>
<a href="/uploads/${a.stored_name}" target="_blank" class="small text-decoration-none" style="word-break:break-all;">${esc(a.original_name)}</a>
<small class="text-secondary ms-2">(${size})</small>
<div class="d-flex align-items-center" style="min-width:0;">
<i class="fas ${icon} me-2" style="font-size:.85rem;flex-shrink:0;"></i>
<a href="${href}"${onclick} target="_blank" class="small text-decoration-none text-truncate" style="color:var(--neptune-accent);">${esc(a.original_name)}</a>
<small class="text-secondary ms-2 flex-shrink-0">(${size})</small>
</div>
<div class="d-flex align-items-center gap-1 flex-shrink-0">
<a href="/download/?file=${encodeURIComponent(a.stored_name)}&mode=download" class="btn btn-outline-primary btn-sm py-0 px-1" title="Download" style="font-size:.6rem;"><i class="fas fa-download"></i></a>
<button class="btn btn-outline-danger btn-sm py-0 px-1" onclick="deleteAttachment(${a.id}, this)" title="Delete file" style="font-size:.6rem;"><i class="fas fa-times"></i></button>
</div>
<button class="btn btn-outline-danger btn-sm py-0 px-1 ms-2" onclick="deleteAttachment(${a.id}, this)" title="Delete file" style="font-size:.65rem;"><i class="fas fa-times"></i></button>
</div>`;
}).join('');
}
@@ -618,6 +625,41 @@ function getFileIcon(mime, name) {
return 'fa-file';
}
function openFileViewer(storedName, originalName) {
const modalEl = document.getElementById('fileViewerModal');
document.getElementById('fileViewerName').textContent = originalName;
document.getElementById('fileViewerDownloadBtn').onclick = () => {
window.open('/download/?file=' + encodeURIComponent(storedName) + '&mode=download', '_blank');
};
const body = document.getElementById('fileViewerBody');
body.innerHTML = '<div class="text-center text-secondary py-5"><div class="spinner-border" role="status"></div><p class="mt-2 small">Loading file...</p></div>';
const modal = new bootstrap.Modal(modalEl);
modal.show();
const ext = originalName.split('.').pop().toLowerCase();
if (ext === 'pdf') {
body.innerHTML = `<iframe src="/download/?file=${encodeURIComponent(storedName)}&mode=view" style="width:100%;min-height:80vh;border:none;"></iframe>`;
return;
}
fetch('/download/?file=' + encodeURIComponent(storedName) + '&mode=view')
.then(r => {
if (!r.ok) throw new Error('Failed to load');
return r.text();
})
.then(text => {
if (ext === 'md') {
body.innerHTML = '<div class="p-3" style="white-space:pre-wrap;font-family:var(--bs-font-monospace);font-size:.85rem;line-height:1.6;color:#e2e8f0;">' + esc(text) + '</div>';
} else {
body.innerHTML = '<div class="p-3" style="white-space:pre-wrap;font-family:var(--bs-font-monospace);font-size:.85rem;line-height:1.6;color:#e2e8f0;">' + esc(text) + '</div>';
}
})
.catch(() => {
body.innerHTML = '<div class="text-center text-danger py-5"><i class="fas fa-exclamation-circle fs-2 mb-2"></i><p class="small">Failed to load file</p></div>';
});
}
async function saveEvent() {
const tags = parseEventTags();
const data = {