fix preview errors

This commit is contained in:
chark1es 2025-02-15 03:27:33 -08:00
parent b4c238de1f
commit 05a92208ce
3 changed files with 466 additions and 289 deletions

View file

@ -3,7 +3,7 @@ import { Icon } from "astro-icon/components";
import { Get } from "../pocketbase/Get";
import { Authentication } from "../pocketbase/Authentication";
import EventEditor from "./Officer_EventManagement/EventEditor";
import FilePreview from "../modals/FilePreview";
import FilePreview from "./Officer_EventManagement/FilePreview";
// Get instances
const get = Get.getInstance();
@ -714,7 +714,7 @@ const currentPage = eventResponse.page;
import { Update } from "../pocketbase/Update";
import { FileManager } from "../pocketbase/FileManager";
import { SendLog } from "../pocketbase/SendLog";
import FilePreview from "../modals/FilePreview";
import FilePreview from "./Officer_EventManagement/FilePreview";
// Add file storage
const selectedFileStorage = new Map<string, File>();
@ -1832,12 +1832,8 @@ const currentPage = eventResponse.page;
const modal = document.getElementById("filePreviewModal") as HTMLDialogElement;
const filePreview = document.getElementById("universalFilePreview") as any;
const previewFileName = document.getElementById("previewFileName");
const loadingSpinner = document.getElementById("previewLoadingSpinner");
if (filePreview && modal && previewFileName && loadingSpinner) {
// Show loading spinner
loadingSpinner.classList.remove("hidden");
if (filePreview && modal && previewFileName) {
// Update the filename display
previewFileName.textContent = filename;
@ -1847,27 +1843,20 @@ const currentPage = eventResponse.page;
// Update the preview component
filePreview.setAttribute("url", url);
filePreview.setAttribute("filename", filename);
// Hide loading spinner after a short delay
setTimeout(() => {
loadingSpinner.classList.add("hidden");
}, 500);
}
};
// Close file preview
window.closeFilePreview = function () {
const modal = document.getElementById("filePreviewModal") as HTMLDialogElement;
const filePreview = document.getElementById("universalFilePreview");
const filePreview = document.getElementById("universalFilePreview") as any;
const previewFileName = document.getElementById("previewFileName");
const filesContent = document.getElementById("filesContent");
if (modal && filePreview && previewFileName) {
// Reset the preview
const event = new CustomEvent("updateFilePreview", {
detail: { url: "", filename: "" },
});
filePreview.dispatchEvent(event);
filePreview.setAttribute("url", "");
filePreview.setAttribute("filename", "");
previewFileName.textContent = "";
modal.close();

View file

@ -4,7 +4,7 @@ import { Authentication } from "../../pocketbase/Authentication";
import { Update } from "../../pocketbase/Update";
import { FileManager } from "../../pocketbase/FileManager";
import { SendLog } from "../../pocketbase/SendLog";
import FilePreview from "../../modals/FilePreview";
import FilePreview from "./FilePreview";
// Extend Window interface
declare global {
@ -129,6 +129,7 @@ export default function EventEditor({ onEventSaved }: EventEditorProps) {
setPreviewFilename(filename);
setShowPreview(true);
};
// Handle form submission
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
@ -256,29 +257,37 @@ export default function EventEditor({ onEventSaved }: EventEditorProps) {
}
};
// Add preview section
const previewSection = showPreview && event?.id && (
<FilePreview
url={fileManager.getFileUrl("events", event.id, previewFilename)}
filename={previewFilename}
onClose={() => {
setShowPreview(false);
const modal = document.getElementById('filePreviewModal') as HTMLDialogElement;
if (modal) {
modal.close();
}
}}
/>
);
return (
<dialog id="editEventModal" className="modal">
{/* Preview Section */}
{previewSection}
{showPreview ? (
<div className="modal-box max-w-4xl">
<div className="flex justify-between items-center mb-4">
<div className="flex items-center gap-3">
<button
className="btn btn-ghost btn-sm"
onClick={() => setShowPreview(false)}
>
Back
</button>
<h3 className="font-bold text-lg truncate">
{previewFilename}
</h3>
</div>
</div>
<div className="relative">
<FilePreview
url={previewUrl}
filename={previewFilename}
/>
</div>
</div>
) : (
<div className="modal-box max-w-2xl">
{/* Main Edit Form Section */}
<div id="editFormSection">
<h3 className="font-bold text-lg mb-4" id="editModalTitle">Edit Event</h3>
<h3 className="font-bold text-lg mb-4" id="editModalTitle">
{event?.id ? 'Edit Event' : 'Add New Event'}
</h3>
<form id="editEventForm" onSubmit={handleSubmit} className="space-y-4">
<input type="hidden" id="editEventId" name="editEventId" value={event?.id || ''} />
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
@ -442,7 +451,14 @@ export default function EventEditor({ onEventSaved }: EventEditorProps) {
<button
type="button"
className="btn btn-ghost btn-xs"
onClick={() => handlePreviewFile(fileManager.getFileUrl("events", event.id, filename), filename)}
onClick={() => {
if (event?.id) {
handlePreviewFile(
fileManager.getFileUrl("events", event.id, filename),
filename
);
}
}}
>
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
<path d="M10 12a2 2 0 100-4 2 2 0 000 4z" />
@ -540,6 +556,7 @@ export default function EventEditor({ onEventSaved }: EventEditorProps) {
</button>
</div>
</div>
)}
<form method="dialog" className="modal-backdrop">
<button>close</button>
</form>

View file

@ -0,0 +1,171 @@
import React, { useEffect, useState } from 'react';
interface FilePreviewProps {
url: string;
filename: string;
}
const FilePreview: React.FC<FilePreviewProps> = ({ url, filename }) => {
const [content, setContent] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
const [fileType, setFileType] = useState<string | null>(null);
useEffect(() => {
if (!url || !filename) {
setContent(null);
setError(null);
setFileType(null);
return;
}
const loadContent = async () => {
setLoading(true);
setError(null);
try {
const response = await fetch(url);
const contentType = response.headers.get('content-type');
setFileType(contentType);
if (contentType?.startsWith('image/')) {
setContent('image');
} else if (contentType?.startsWith('video/')) {
setContent('video');
} else if (contentType?.startsWith('application/pdf')) {
setContent('pdf');
} else if (contentType?.startsWith('text/')) {
const text = await response.text();
// Truncate text if it's too long (e.g., more than 100KB)
if (text.length > 100000) {
setContent(text.substring(0, 100000) + '\n\n... Content truncated. Please download the file to view the complete content.');
} else {
setContent(text);
}
} else if (filename.toLowerCase().endsWith('.mp4')) {
// Fallback for video files when content-type might not be correct
setContent('video');
} else {
setError(`This file type (${contentType || 'unknown'}) is not supported for preview. Please download the file to view it.`);
}
} catch (err) {
setError('Failed to load file');
console.error('Error loading file:', err);
} finally {
setLoading(false);
}
};
loadContent();
}, [url, filename]);
const handleDownload = async () => {
try {
const response = await fetch(url);
const blob = await response.blob();
const downloadUrl = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = downloadUrl;
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(downloadUrl);
} catch (err) {
console.error('Error downloading file:', err);
alert('Failed to download file. Please try again.');
}
};
return (
<div className="space-y-4">
{/* Header with filename and download button */}
<div className="flex justify-between items-center bg-base-200 p-3 rounded-lg">
<div className="flex items-center gap-2 flex-1 min-w-0">
<span className="truncate font-medium">{filename}</span>
{fileType && (
<span className="badge badge-sm">{fileType.split('/')[1]}</span>
)}
</div>
<button
onClick={handleDownload}
className="btn btn-sm btn-ghost gap-2"
>
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd" d="M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z" clipRule="evenodd" />
</svg>
Download
</button>
</div>
{/* Preview content */}
<div className="preview-content">
{loading && (
<div className="flex justify-center items-center p-8">
<span className="loading loading-spinner loading-lg"></span>
</div>
)}
{error && (
<div className="flex flex-col items-center justify-center p-8 bg-base-200 rounded-lg text-center space-y-4">
<div className="bg-warning/20 p-4 rounded-full">
<svg xmlns="http://www.w3.org/2000/svg" className="h-12 w-12 text-warning" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
</div>
<div className="space-y-2">
<h3 className="text-lg font-semibold">Preview Unavailable</h3>
<p className="text-base-content/70 max-w-md">{error}</p>
</div>
<button
onClick={handleDownload}
className="btn btn-warning btn-sm gap-2 mt-4"
>
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd" d="M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z" clipRule="evenodd" />
</svg>
Download File Instead
</button>
</div>
)}
{!loading && !error && content === 'image' && (
<div className="flex justify-center">
<img src={url} alt={filename} className="max-w-full h-auto rounded-lg" />
</div>
)}
{!loading && !error && content === 'video' && (
<div className="flex justify-center">
<video
controls
className="max-w-full rounded-lg"
style={{ maxHeight: '600px' }}
>
<source src={url} type="video/mp4" />
Your browser does not support the video tag.
</video>
</div>
)}
{!loading && !error && content === 'pdf' && (
<div className="w-full h-[600px]">
<iframe
src={url}
className="w-full h-full rounded-lg"
title={filename}
></iframe>
</div>
)}
{!loading && !error && content && !['image', 'video', 'pdf'].includes(content) && (
<div className="mockup-code bg-base-200 text-base-content overflow-x-auto max-h-[600px]">
<pre className="p-4"><code>{content}</code></pre>
</div>
)}
</div>
</div>
);
};
export default FilePreview;