diff --git a/surfsense_web/components/report-panel/report-panel.tsx b/surfsense_web/components/report-panel/report-panel.tsx index de1527a77..582f341c2 100644 --- a/surfsense_web/components/report-panel/report-panel.tsx +++ b/surfsense_web/components/report-panel/report-panel.tsx @@ -372,7 +372,7 @@ export function ReportPanelContent({ {isLoading ? ( ) : error || !reportContent ? ( -
+

Failed to load report

{error || "An unknown error occurred"}

diff --git a/surfsense_web/components/tool-ui/generate-resume.tsx b/surfsense_web/components/tool-ui/generate-resume.tsx index a2c1aa5c4..d1ea95ea9 100644 --- a/surfsense_web/components/tool-ui/generate-resume.tsx +++ b/surfsense_web/components/tool-ui/generate-resume.tsx @@ -2,13 +2,22 @@ import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { useAtomValue, useSetAtom } from "jotai"; -import { FileTextIcon } from "lucide-react"; import { useParams, usePathname } from "next/navigation"; -import { useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { Document, Page, pdfjs } from "react-pdf"; +import "react-pdf/dist/Page/AnnotationLayer.css"; +import "react-pdf/dist/Page/TextLayer.css"; import { z } from "zod"; import { openReportPanelAtom, reportPanelAtom } from "@/atoms/chat/report-panel.atom"; import { TextShimmerLoader } from "@/components/prompt-kit/loader"; +import { Spinner } from "@/components/ui/spinner"; import { useMediaQuery } from "@/hooks/use-media-query"; +import { getAuthHeaders } from "@/lib/auth-utils"; + +pdfjs.GlobalWorkerOptions.workerSrc = new URL( + "pdfjs-dist/build/pdf.worker.min.mjs", + import.meta.url +).toString(); const GenerateResumeArgsSchema = z.object({ user_info: z.string(), @@ -35,7 +44,7 @@ function ResumeGeneratingState() {

Resume

- +
@@ -96,10 +105,13 @@ function ResumeCard({ const panelState = useAtomValue(reportPanelAtom); const isDesktop = useMediaQuery("(min-width: 768px)"); const autoOpenedRef = useRef(false); - const [pdfThumbnailUrl, setPdfThumbnailUrl] = useState(null); + const [pdfUrl, setPdfUrl] = useState(null); + const [pdfReady, setPdfReady] = useState(false); + const [pdfError, setPdfError] = useState(false); + const documentOptionsRef = useRef({ httpHeaders: getAuthHeaders() }); useEffect(() => { - setPdfThumbnailUrl( + setPdfUrl( `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/reports/${reportId}/preview` ); @@ -114,6 +126,15 @@ function ResumeCard({ } }, [reportId, title, shareToken, autoOpen, isDesktop, openPanel]); + const onPdfLoadSuccess = useCallback(() => { + setPdfReady(true); + }, []); + + const onPdfLoadError = useCallback(() => { + setPdfReady(true); + setPdfError(true); + }, []); + const isActive = panelState.isOpen && panelState.reportId === reportId; const handleOpen = () => { @@ -129,31 +150,55 @@ function ResumeCard({
-