fix file viewer buttons
This commit is contained in:
parent
fc39192f61
commit
98ee9f3710
2 changed files with 86 additions and 126 deletions
|
@ -1202,6 +1202,7 @@ export class EventAuth {
|
||||||
const fileUrl = this.pb.files.getURL(event, file);
|
const fileUrl = this.pb.files.getURL(event, file);
|
||||||
const fileName = this.getFileNameFromUrl(file);
|
const fileName = this.getFileNameFromUrl(file);
|
||||||
const fileExt = fileName.split('.').pop()?.toLowerCase() || '';
|
const fileExt = fileName.split('.').pop()?.toLowerCase() || '';
|
||||||
|
const nameWithoutExt = fileName.substring(0, fileName.lastIndexOf('.'));
|
||||||
|
|
||||||
const fileItem = document.createElement("div");
|
const fileItem = document.createElement("div");
|
||||||
fileItem.className = "bg-base-200 rounded-lg overflow-hidden";
|
fileItem.className = "bg-base-200 rounded-lg overflow-hidden";
|
||||||
|
@ -1214,12 +1215,6 @@ export class EventAuth {
|
||||||
<img src="${fileUrl}" alt="${fileName}" class="w-full h-full object-contain">
|
<img src="${fileUrl}" alt="${fileName}" class="w-full h-full object-contain">
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
} else if (fileExt === 'pdf') {
|
|
||||||
previewHtml = `
|
|
||||||
<div class="aspect-video bg-base-300 overflow-hidden">
|
|
||||||
<iframe src="${fileUrl}" class="w-full h-full"></iframe>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
} else {
|
} else {
|
||||||
// For other file types, show an icon based on type
|
// For other file types, show an icon based on type
|
||||||
const iconHtml = fileExt === 'txt' || fileExt === 'md'
|
const iconHtml = fileExt === 'txt' || fileExt === 'md'
|
||||||
|
@ -1239,23 +1234,26 @@ export class EventAuth {
|
||||||
|
|
||||||
fileItem.innerHTML = `
|
fileItem.innerHTML = `
|
||||||
${previewHtml}
|
${previewHtml}
|
||||||
<div class="p-3 flex items-center justify-between gap-2">
|
<div class="card-body p-4">
|
||||||
<div class="flex-1 min-w-0">
|
<h3 class="card-title text-sm flex items-center justify-between gap-2" title="${fileName}">
|
||||||
<span class="text-sm truncate block" title="${fileName}">${fileName}</span>
|
<span class="truncate min-w-0">${nameWithoutExt}</span>
|
||||||
</div>
|
<span class="text-base-content/50 text-xs uppercase font-mono shrink-0">${fileExt}</span>
|
||||||
<div class="flex gap-2 shrink-0">
|
</h3>
|
||||||
<a href="${fileUrl}" target="_blank" class="btn btn-ghost btn-xs" title="Open in new tab">
|
</div>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
|
<div class="border-t border-base-300 grid grid-cols-2">
|
||||||
<path d="M11 3a1 1 0 100 2h2.586l-6.293 6.293a1 1 0 101.414 1.414L15 6.414V9a1 1 0 102 0V4a1 1 0 00-1-1h-5z" />
|
<a href="${fileUrl}" target="_blank" class="btn btn-ghost btn-sm rounded-none rounded-bl-lg gap-2 h-12">
|
||||||
<path d="M5 5a2 2 0 00-2 2v8a2 2 0 002 2h8a2 2 0 002-2v-3a1 1 0 10-2 0v3H5V7h3a1 1 0 000-2H5z" />
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
|
||||||
</svg>
|
<path d="M11 3a1 1 0 100 2h2.586l-6.293 6.293a1 1 0 101.414 1.414L15 6.414V9a1 1 0 102 0V4a1 1 0 00-1-1h-5z" />
|
||||||
</a>
|
<path d="M5 5a2 2 0 00-2 2v8a2 2 0 002 2h8a2 2 0 002-2v-3a1 1 0 10-2 0v3H5V7h3a1 1 0 000-2H5z" />
|
||||||
<button class="btn btn-ghost btn-xs text-error" title="Remove file" data-file="${file}">
|
</svg>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
|
Open
|
||||||
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
|
</a>
|
||||||
</svg>
|
<button class="btn btn-ghost btn-sm text-error rounded-none rounded-br-lg gap-2 h-12 border-l border-base-300" title="Remove file" data-file="${file}">
|
||||||
</button>
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
|
||||||
</div>
|
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
Remove
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -1263,7 +1261,7 @@ export class EventAuth {
|
||||||
const deleteButton = fileItem.querySelector('.text-error');
|
const deleteButton = fileItem.querySelector('.text-error');
|
||||||
if (deleteButton) {
|
if (deleteButton) {
|
||||||
deleteButton.addEventListener('click', async (e) => {
|
deleteButton.addEventListener('click', async (e) => {
|
||||||
e.preventDefault(); // Prevent any form submission
|
e.preventDefault();
|
||||||
if (confirm('Are you sure you want to remove this file?')) {
|
if (confirm('Are you sure you want to remove this file?')) {
|
||||||
try {
|
try {
|
||||||
const fileToRemove = deleteButton.getAttribute('data-file');
|
const fileToRemove = deleteButton.getAttribute('data-file');
|
||||||
|
@ -1307,14 +1305,14 @@ export class EventAuth {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleViewFiles(eventId: string) {
|
private async handleViewFiles(eventId: string): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const event = await this.pb.collection("events").getOne(eventId);
|
const event = await this.pb.collection("events").getOne<Event>(eventId);
|
||||||
|
|
||||||
// Create and show modal
|
// Create and show modal
|
||||||
const modal = document.createElement("dialog");
|
const modal = document.createElement("dialog");
|
||||||
modal.className = "modal";
|
modal.className = "modal";
|
||||||
modal.innerHTML = `
|
const modalContent = `
|
||||||
<div class="modal-box max-w-5xl">
|
<div class="modal-box max-w-5xl">
|
||||||
<div class="flex justify-between items-center mb-4">
|
<div class="flex justify-between items-center mb-4">
|
||||||
<h3 class="font-bold text-lg">Files for ${event.event_name}</h3>
|
<h3 class="font-bold text-lg">Files for ${event.event_name}</h3>
|
||||||
|
@ -1330,7 +1328,7 @@ export class EventAuth {
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
${event.files && Array.isArray(event.files) && event.files.length > 0
|
${event.files && Array.isArray(event.files) && event.files.length > 0
|
||||||
? event.files.map(file => {
|
? event.files.map((file: string) => {
|
||||||
const fileUrl = this.pb.files.getURL(event, file);
|
const fileUrl = this.pb.files.getURL(event, file);
|
||||||
const fileName = this.getFileNameFromUrl(file);
|
const fileName = this.getFileNameFromUrl(file);
|
||||||
const fileExt = fileName.split('.').pop()?.toLowerCase() || '';
|
const fileExt = fileName.split('.').pop()?.toLowerCase() || '';
|
||||||
|
@ -1353,29 +1351,34 @@ export class EventAuth {
|
||||||
</svg>`;
|
</svg>`;
|
||||||
|
|
||||||
previewHtml = `
|
previewHtml = `
|
||||||
<div class="aspect-video bg-base-300 rounded-t-lg flex items-center justify-center">
|
<div class="aspect-video bg-base-300 flex items-center justify-center">
|
||||||
${iconHtml}
|
${iconHtml}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<button class="preview-file group bg-base-200 rounded-lg transition-colors w-full text-left hover:bg-base-300"
|
<div class="card bg-base-200">
|
||||||
data-url="${fileUrl}"
|
|
||||||
data-filename="${fileName}"
|
|
||||||
data-ext="${fileExt}">
|
|
||||||
${previewHtml}
|
${previewHtml}
|
||||||
<div class="p-3 flex items-center gap-2">
|
<div class="card-body p-4">
|
||||||
<span class="text-sm truncate flex-1" title="${fileName}">${fileName}</span>
|
<h3 class="card-title text-sm flex items-center justify-between gap-2" title="${fileName}">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 opacity-50 group-hover:opacity-100 transition-opacity" viewBox="0 0 20 20" fill="currentColor">
|
<span class="truncate min-w-0">${fileName.substring(0, fileName.lastIndexOf("."))}</span>
|
||||||
<path d="M11 3a1 1 0 100 2h2.586l-6.293 6.293a1 1 0 101.414 1.414L15 6.414V9a1 1 0 102 0V4a1 1 0 00-1-1h-5z" />
|
<span class="text-base-content/50 text-xs uppercase font-mono shrink-0">${fileExt}</span>
|
||||||
<path d="M5 5a2 2 0 00-2 2v8a2 2 0 002 2h8a2 2 0 002-2v-3a1 1 0 10-2 0v3H5V7h3a1 1 0 000-2H5z" />
|
</h3>
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
</button>
|
<div class="border-t border-base-300">
|
||||||
|
<a href="${fileUrl}" target="_blank" class="btn btn-ghost btn-sm w-full rounded-none rounded-b-lg gap-2 h-12">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
|
||||||
|
<path d="M11 3a1 1 0 100 2h2.586l-6.293 6.293a1 1 0 101.414 1.414L15 6.414V9a1 1 0 102 0V4a1 1 0 00-1-1h-5z" />
|
||||||
|
<path d="M5 5a2 2 0 00-2 2v8a2 2 0 002 2h8a2 2 0 002-2v-3a1 1 0 10-2 0v3H5V7h3a1 1 0 000-2H5z" />
|
||||||
|
</svg>
|
||||||
|
Open in New Tab
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}).join("")
|
}).join("")
|
||||||
: '<div class="col-span-full text-center py-4 text-opacity-50">No files available</div>'
|
: `<div class="col-span-full text-center py-4 opacity-70">No files available</div>`
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-action">
|
<div class="modal-action">
|
||||||
|
@ -1387,90 +1390,44 @@ export class EventAuth {
|
||||||
<form method="dialog" class="modal-backdrop">
|
<form method="dialog" class="modal-backdrop">
|
||||||
<button>close</button>
|
<button>close</button>
|
||||||
</form>
|
</form>
|
||||||
`;
|
</dialog>`;
|
||||||
|
|
||||||
|
modal.innerHTML = modalContent;
|
||||||
document.body.appendChild(modal);
|
document.body.appendChild(modal);
|
||||||
modal.showModal();
|
modal.showModal();
|
||||||
|
|
||||||
// Add preview functionality
|
// Add download all functionality
|
||||||
const previewButtons = modal.querySelectorAll('.preview-file');
|
const downloadAllButton = modal.querySelector('.download-all') as HTMLButtonElement;
|
||||||
previewButtons.forEach(button => {
|
if (downloadAllButton) {
|
||||||
button.addEventListener('click', () => {
|
downloadAllButton.addEventListener('click', async () => {
|
||||||
const url = (button as HTMLButtonElement).dataset.url;
|
|
||||||
const fileName = (button as HTMLButtonElement).dataset.filename;
|
|
||||||
const ext = (button as HTMLButtonElement).dataset.ext;
|
|
||||||
if (url && fileName) {
|
|
||||||
if (ext === 'pdf') {
|
|
||||||
window.open(url, '_blank');
|
|
||||||
} else {
|
|
||||||
document.dispatchEvent(new CustomEvent('showFilePreview', {
|
|
||||||
detail: { url, fileName }
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add download functionality
|
|
||||||
const downloadButton = modal.querySelector('.download-all');
|
|
||||||
if (downloadButton && event.files && Array.isArray(event.files)) {
|
|
||||||
downloadButton.addEventListener('click', async () => {
|
|
||||||
try {
|
try {
|
||||||
if (event.files.length === 1) {
|
// Create a new JSZip instance
|
||||||
// For single file, download directly
|
const zip = new JSZip();
|
||||||
const fileUrl = this.pb.files.getURL(event, event.files[0]);
|
|
||||||
const fileName = this.getFileNameFromUrl(event.files[0]);
|
// Add each file to the zip
|
||||||
|
for (const file of event.files) {
|
||||||
|
const fileUrl = this.pb.files.getURL(event, file);
|
||||||
|
const fileName = this.getFileNameFromUrl(file);
|
||||||
const response = await fetch(fileUrl);
|
const response = await fetch(fileUrl);
|
||||||
const blob = await response.blob();
|
const blob = await response.blob();
|
||||||
const url = window.URL.createObjectURL(blob);
|
zip.file(fileName, blob);
|
||||||
const a = document.createElement('a');
|
|
||||||
a.href = url;
|
|
||||||
a.download = fileName;
|
|
||||||
document.body.appendChild(a);
|
|
||||||
a.click();
|
|
||||||
window.URL.revokeObjectURL(url);
|
|
||||||
document.body.removeChild(a);
|
|
||||||
} else {
|
|
||||||
// For multiple files, create a zip
|
|
||||||
const zip = new JSZip();
|
|
||||||
|
|
||||||
// Show loading state
|
|
||||||
downloadButton.classList.add('loading');
|
|
||||||
downloadButton.setAttribute('disabled', 'true');
|
|
||||||
|
|
||||||
// Download all files and add to zip
|
|
||||||
await Promise.all(event.files.map(async (file: string) => {
|
|
||||||
const fileUrl = this.pb.files.getURL(event, file);
|
|
||||||
const fileName = this.getFileNameFromUrl(file);
|
|
||||||
const response = await fetch(fileUrl);
|
|
||||||
const blob = await response.blob();
|
|
||||||
zip.file(fileName, blob);
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Generate and download zip file
|
|
||||||
const zipBlob = await zip.generateAsync({ type: 'blob' });
|
|
||||||
const url = window.URL.createObjectURL(zipBlob);
|
|
||||||
const a = document.createElement('a');
|
|
||||||
a.href = url;
|
|
||||||
a.download = `${event.event_name || 'event'}_files.zip`;
|
|
||||||
document.body.appendChild(a);
|
|
||||||
a.click();
|
|
||||||
window.URL.revokeObjectURL(url);
|
|
||||||
document.body.removeChild(a);
|
|
||||||
|
|
||||||
// Reset button state
|
|
||||||
downloadButton.classList.remove('loading');
|
|
||||||
downloadButton.removeAttribute('disabled');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate the zip file
|
||||||
|
const content = await zip.generateAsync({ type: "blob" });
|
||||||
|
|
||||||
|
// Create a download link and trigger it
|
||||||
|
const downloadUrl = URL.createObjectURL(content);
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = downloadUrl;
|
||||||
|
link.download = `${event.event_name} Files.zip`;
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
URL.revokeObjectURL(downloadUrl);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to download files:', err);
|
console.error('Failed to download files:', err);
|
||||||
alert('Failed to download files. Please try again.');
|
alert('Failed to download files. Please try again.');
|
||||||
|
|
||||||
// Reset button state on error
|
|
||||||
if (downloadButton) {
|
|
||||||
downloadButton.classList.remove('loading');
|
|
||||||
downloadButton.removeAttribute('disabled');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -568,16 +568,19 @@
|
||||||
<div class="card bg-base-200">
|
<div class="card bg-base-200">
|
||||||
${preview}
|
${preview}
|
||||||
<div class="card-body p-4">
|
<div class="card-body p-4">
|
||||||
<h3 class="card-title text-sm truncate" title="${fileName}">${fileName}</h3>
|
<h3 class="card-title text-sm flex items-center justify-between gap-2" title="${fileName}">
|
||||||
<div class="card-actions justify-end">
|
<span class="truncate min-w-0">${fileName.substring(0, fileName.lastIndexOf("."))}</span>
|
||||||
<a href="${fileUrl}" target="_blank" class="btn btn-ghost btn-sm gap-2">
|
<span class="text-base-content/50 text-xs uppercase font-mono shrink-0">${fileExt}</span>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
|
</h3>
|
||||||
<path d="M11 3a1 1 0 100 2h2.586l-6.293 6.293a1 1 0 101.414 1.414L15 6.414V9a1 1 0 102 0V4a1 1 0 00-1-1h-5z" />
|
</div>
|
||||||
<path d="M5 5a2 2 0 00-2 2v8a2 2 0 002 2h8a2 2 0 002-2v-3a1 1 0 10-2 0v3H5V7h3a1 1 0 000-2H5z" />
|
<div class="border-t border-base-300">
|
||||||
</svg>
|
<a href="${fileUrl}" target="_blank" class="btn btn-ghost btn-sm w-full rounded-none rounded-b-lg gap-2 h-12">
|
||||||
Open
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
|
||||||
</a>
|
<path d="M11 3a1 1 0 100 2h2.586l-6.293 6.293a1 1 0 101.414 1.414L15 6.414V9a1 1 0 102 0V4a1 1 0 00-1-1h-5z" />
|
||||||
</div>
|
<path d="M5 5a2 2 0 00-2 2v8a2 2 0 002 2h8a2 2 0 002-2v-3a1 1 0 10-2 0v3H5V7h3a1 1 0 000-2H5z" />
|
||||||
|
</svg>
|
||||||
|
Open in New Tab
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
Loading…
Reference in a new issue