This commit is contained in:
@@ -282,4 +282,15 @@ kbd {
|
||||
.attachment-item a:hover {
|
||||
color: #60a5fa;
|
||||
text-decoration: underline !important;
|
||||
}
|
||||
|
||||
/* File Viewer */
|
||||
#fileViewerModal .modal-content {
|
||||
border-color: var(--neptune-border);
|
||||
}
|
||||
|
||||
#fileViewerBody pre {
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
@@ -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 = {
|
||||
|
||||
@@ -527,6 +527,24 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ==================== FILE VIEWER MODAL ==================== -->
|
||||
<div class="modal fade" id="fileViewerModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-xl modal-dialog-centered modal-dialog-scrollable">
|
||||
<div class="modal-content bg-dark">
|
||||
<div class="modal-header border-secondary py-2">
|
||||
<h6 class="modal-title" id="fileViewerTitle"><i class="fas fa-file me-1"></i> <span id="fileViewerName"></span></h6>
|
||||
<div class="d-flex align-items-center gap-1">
|
||||
<button type="button" class="btn btn-outline-primary btn-sm" id="fileViewerDownloadBtn"><i class="fas fa-download me-1"></i>Download</button>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-body p-0" id="fileViewerBody" style="min-height:60vh;max-height:80vh;overflow:auto;background:#0d1117;">
|
||||
<div class="text-center text-secondary py-5"><div class="spinner-border" role="status"></div><p class="mt-2 small">Loading file...</p></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ==================== LOGIN OVERLAY ==================== -->
|
||||
<div id="loginOverlay" style="position:fixed;top:0;left:0;width:100%;height:100%;background:#0a0e1a;z-index:9999;display:flex;align-items:center;justify-content:center;">
|
||||
<div style="text-align:center;max-width:400px;padding:2rem;">
|
||||
|
||||
Reference in New Issue
Block a user