mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-23 19:05:16 +02:00
refactor: update PDF viewer and report card components for improved accessibility and UI consistency
This commit is contained in:
parent
45752a7e73
commit
4d577a20a9
3 changed files with 27 additions and 46 deletions
|
|
@ -1,11 +1,12 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { ChevronLeftIcon, ChevronRightIcon, ZoomInIcon, ZoomOutIcon } from "lucide-react";
|
import { ChevronLeftIcon, ChevronRightIcon, ZoomInIcon, ZoomOutIcon } from "lucide-react";
|
||||||
import { useCallback, useMemo, useRef, useState } from "react";
|
import { useCallback, useRef, useState } from "react";
|
||||||
import { Document, Page, pdfjs } from "react-pdf";
|
import { Document, Page, pdfjs } from "react-pdf";
|
||||||
import "react-pdf/dist/Page/AnnotationLayer.css";
|
import "react-pdf/dist/Page/AnnotationLayer.css";
|
||||||
import "react-pdf/dist/Page/TextLayer.css";
|
import "react-pdf/dist/Page/TextLayer.css";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Spinner } from "@/components/ui/spinner";
|
||||||
import { getAuthHeaders } from "@/lib/auth-utils";
|
import { getAuthHeaders } from "@/lib/auth-utils";
|
||||||
|
|
||||||
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
|
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
|
||||||
|
|
@ -27,8 +28,7 @@ export function PdfViewer({ pdfUrl }: PdfViewerProps) {
|
||||||
const [scale, setScale] = useState(1);
|
const [scale, setScale] = useState(1);
|
||||||
const [loadError, setLoadError] = useState<string | null>(null);
|
const [loadError, setLoadError] = useState<string | null>(null);
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
const documentOptionsRef = useRef({ httpHeaders: getAuthHeaders() });
|
||||||
const documentOptions = useMemo(() => ({ httpHeaders: getAuthHeaders() }), []);
|
|
||||||
|
|
||||||
const onDocumentLoadSuccess = useCallback(({ numPages }: { numPages: number }) => {
|
const onDocumentLoadSuccess = useCallback(({ numPages }: { numPages: number }) => {
|
||||||
setNumPages(numPages);
|
setNumPages(numPages);
|
||||||
|
|
@ -109,15 +109,15 @@ export function PdfViewer({ pdfUrl }: PdfViewerProps) {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* PDF content */}
|
{/* PDF content */}
|
||||||
<div ref={containerRef} className="flex-1 overflow-auto flex justify-center bg-muted/30 p-4">
|
<div ref={containerRef} className="flex-1 overflow-auto flex justify-center bg-sidebar p-0">
|
||||||
<Document
|
<Document
|
||||||
file={pdfUrl}
|
file={pdfUrl}
|
||||||
onLoadSuccess={onDocumentLoadSuccess}
|
onLoadSuccess={onDocumentLoadSuccess}
|
||||||
onLoadError={onDocumentLoadError}
|
onLoadError={onDocumentLoadError}
|
||||||
options={documentOptions}
|
options={documentOptionsRef.current}
|
||||||
loading={
|
loading={
|
||||||
<div className="flex items-center justify-center h-64">
|
<div className="flex items-center justify-center h-64 text-sidebar-foreground">
|
||||||
<div className="size-6 border-2 border-primary border-t-transparent rounded-full animate-spin" />
|
<Spinner size="md" />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -215,17 +215,9 @@ function ReportCard({
|
||||||
<div
|
<div
|
||||||
className={`my-4 max-w-lg overflow-hidden rounded-2xl border bg-muted/30 transition-[box-shadow] duration-300 ${isActive ? "ring-1 ring-primary/50" : ""}`}
|
className={`my-4 max-w-lg overflow-hidden rounded-2xl border bg-muted/30 transition-[box-shadow] duration-300 ${isActive ? "ring-1 ring-primary/50" : ""}`}
|
||||||
>
|
>
|
||||||
{/* biome-ignore lint/a11y/useSemanticElements: can't use <button> here because PlateEditor renders nested <button> elements (e.g. CopyButton) */}
|
<button
|
||||||
<div
|
type="button"
|
||||||
role="button"
|
|
||||||
tabIndex={0}
|
|
||||||
onClick={handleOpen}
|
onClick={handleOpen}
|
||||||
onKeyDown={(e) => {
|
|
||||||
if (e.key === "Enter" || e.key === " ") {
|
|
||||||
e.preventDefault();
|
|
||||||
handleOpen();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
className="w-full text-left transition-colors hover:bg-muted/50 focus:outline-none focus-visible:outline-none cursor-pointer"
|
className="w-full text-left transition-colors hover:bg-muted/50 focus:outline-none focus-visible:outline-none cursor-pointer"
|
||||||
>
|
>
|
||||||
<div className="px-5 pt-5 pb-4 select-none">
|
<div className="px-5 pt-5 pb-4 select-none">
|
||||||
|
|
@ -272,7 +264,7 @@ function ReportCard({
|
||||||
<p className="text-sm text-muted-foreground italic">No content available</p>
|
<p className="text-sm text-muted-foreground italic">No content available</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,6 @@ function ResumeErrorState({ title, error }: { title: string; error: string }) {
|
||||||
<div className="my-4 max-w-lg overflow-hidden rounded-2xl border bg-muted/30 select-none">
|
<div className="my-4 max-w-lg overflow-hidden rounded-2xl border bg-muted/30 select-none">
|
||||||
<div className="px-5 pt-5 pb-4">
|
<div className="px-5 pt-5 pb-4">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<FileTextIcon className="size-4 text-destructive" />
|
|
||||||
<p className="text-sm font-semibold text-destructive">Resume Generation Failed</p>
|
<p className="text-sm font-semibold text-destructive">Resume Generation Failed</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -74,7 +73,6 @@ function ResumeCancelledState() {
|
||||||
<div className="my-4 max-w-lg overflow-hidden rounded-2xl border bg-muted/30 select-none">
|
<div className="my-4 max-w-lg overflow-hidden rounded-2xl border bg-muted/30 select-none">
|
||||||
<div className="px-5 pt-5 pb-4">
|
<div className="px-5 pt-5 pb-4">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<FileTextIcon className="size-4 text-muted-foreground" />
|
|
||||||
<p className="text-sm font-semibold text-muted-foreground">Resume Cancelled</p>
|
<p className="text-sm font-semibold text-muted-foreground">Resume Cancelled</p>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-muted-foreground mt-0.5">Resume generation was cancelled</p>
|
<p className="text-xs text-muted-foreground mt-0.5">Resume generation was cancelled</p>
|
||||||
|
|
@ -131,22 +129,13 @@ function ResumeCard({
|
||||||
<div
|
<div
|
||||||
className={`my-4 max-w-lg overflow-hidden rounded-2xl border bg-muted/30 transition-[box-shadow] duration-300 ${isActive ? "ring-1 ring-primary/50" : ""}`}
|
className={`my-4 max-w-lg overflow-hidden rounded-2xl border bg-muted/30 transition-[box-shadow] duration-300 ${isActive ? "ring-1 ring-primary/50" : ""}`}
|
||||||
>
|
>
|
||||||
{/* biome-ignore lint/a11y/useSemanticElements: nested interactive content */}
|
<button
|
||||||
<div
|
type="button"
|
||||||
role="button"
|
|
||||||
tabIndex={0}
|
|
||||||
onClick={handleOpen}
|
onClick={handleOpen}
|
||||||
onKeyDown={(e) => {
|
|
||||||
if (e.key === "Enter" || e.key === " ") {
|
|
||||||
e.preventDefault();
|
|
||||||
handleOpen();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
className="w-full text-left transition-colors hover:bg-muted/50 focus:outline-none focus-visible:outline-none cursor-pointer"
|
className="w-full text-left transition-colors hover:bg-muted/50 focus:outline-none focus-visible:outline-none cursor-pointer"
|
||||||
>
|
>
|
||||||
<div className="px-5 pt-5 pb-4 select-none">
|
<div className="px-5 pt-5 pb-4 select-none">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<FileTextIcon className="size-4 text-primary" />
|
|
||||||
<p className="text-sm font-semibold text-foreground line-clamp-2">{title}</p>
|
<p className="text-sm font-semibold text-foreground line-clamp-2">{title}</p>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-muted-foreground mt-0.5">Resume • Click to preview</p>
|
<p className="text-xs text-muted-foreground mt-0.5">Resume • Click to preview</p>
|
||||||
|
|
@ -157,7 +146,7 @@ function ResumeCard({
|
||||||
<div className="px-5 pt-3 pb-4 flex items-center justify-center">
|
<div className="px-5 pt-3 pb-4 flex items-center justify-center">
|
||||||
{pdfThumbnailUrl ? (
|
{pdfThumbnailUrl ? (
|
||||||
<div className="flex items-center gap-3 text-muted-foreground">
|
<div className="flex items-center gap-3 text-muted-foreground">
|
||||||
<FileTextIcon className="size-8 text-primary/40" />
|
<FileTextIcon className="size-8 text-primary" />
|
||||||
<span className="text-sm">PDF Resume ready — click to view</span>
|
<span className="text-sm">PDF Resume ready — click to view</span>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -168,7 +157,7 @@ function ResumeCard({
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue