Spaces:
Runtime error
Runtime error
| "use client"; | |
| import { useState, useEffect } from 'react'; | |
| export default function PdfViewer({ pdfUrl, pageNumber }) { | |
| const [loading, setLoading] = useState(true); | |
| const [error, setError] = useState(false); | |
| // Reset loading state when URL or page changes | |
| useEffect(() => { | |
| setLoading(true); | |
| setError(false); | |
| }, [pdfUrl, pageNumber]); | |
| if (!pdfUrl) { | |
| return ( | |
| <div className="pdf-placeholder"> | |
| <p>No PDF available for this document.</p> | |
| </div> | |
| ); | |
| } | |
| // PDF pages in our data are 0-indexed; PDF.js viewer expects 1-indexed pages | |
| const viewerPage = (pageNumber ?? 0) + 1; | |
| // Proxy the PDF through our own API to bypass CORS restrictions. | |
| // Then use the browser's built-in PDF viewer via <object> tag. | |
| const proxyUrl = `/api/pdf-proxy?url=${encodeURIComponent(pdfUrl)}#page=${viewerPage}&zoom=75`; | |
| return ( | |
| <div className="pdf-container"> | |
| {loading && !error && ( | |
| <div className="pdf-loading"> | |
| <div className="loading-spinner" /> | |
| <p>Loading PDF...</p> | |
| <p className="pdf-loading-hint">This may take a moment for large documents.</p> | |
| </div> | |
| )} | |
| {error && ( | |
| <div className="pdf-error"> | |
| <p>⚠️ Failed to load PDF</p> | |
| <p className="pdf-error-hint">The document may be unavailable or too large.</p> | |
| <a href={pdfUrl} target="_blank" rel="noopener noreferrer" className="btn btn-secondary"> | |
| Open PDF directly ↗ | |
| </a> | |
| </div> | |
| )} | |
| <iframe | |
| key={`pdf-${pdfUrl}-page-${viewerPage}`} | |
| src={proxyUrl} | |
| className="pdf-frame" | |
| title={`PDF Page ${viewerPage}`} | |
| allow="fullscreen" | |
| style={{ display: loading && !error ? 'none' : 'block' }} | |
| onLoad={() => setLoading(false)} | |
| onError={() => { setLoading(false); setError(true); }} | |
| /> | |
| </div> | |
| ); | |
| } | |