import { FormSectionCard, FormSectionGrid } from "@repo/rdx-ui/components"; import { Alert, AlertDescription, AlertTitle, Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Spinner, } from "@repo/shadcn-ui/components"; import { cn } from "@repo/shadcn-ui/lib/utils"; import { DownloadIcon, ExternalLinkIcon, EyeIcon, PrinterIcon, ZoomInIcon, ZoomOutIcon, } from "lucide-react"; import { useEffect, useMemo, useRef, useState } from "react"; const DEFAULT_ZOOM_OPTIONS = [50, 75, 100, 125, 150]; const FIT_ZOOM_VALUE = "fit"; const PDF_PAGE_WIDTH = 794; const PDF_PAGE_HEIGHT = 1123; export interface PDFViewerLabels { zoomOut: string; zoomIn: string; zoomSelect: string; fit: string; open: string; download: string; print: string; loading: string; refreshing: string; errorTitle: string; retry: string; emptyTitle: string; emptyDescription: string; } type PDFViewerProps = { src?: string | null; title?: string; description?: string; className?: string; variant?: "panel" | "native-inline"; showOpenAction?: boolean; showDownloadAction?: boolean; showPrinterAction?: boolean; loading?: boolean; isRefreshing?: boolean; error?: string | null; defaultZoom?: number | "fit"; zoomOptions?: number[]; onOpen?: () => void; onDownload?: () => void; onPrint?: () => void; onRetry?: () => void; labels?: Partial; }; const clampZoom = (value: number, zoomOptions: number[]) => { const min = Math.min(...zoomOptions); const max = Math.max(...zoomOptions); return Math.min(Math.max(value, min), max); }; export function PDFViewer({ src, title = "Vista previa", description = "Documento PDF embebido en la pantalla.", className, variant = "panel", showOpenAction = true, showDownloadAction = true, showPrinterAction = true, loading = false, isRefreshing = false, error = null, defaultZoom = FIT_ZOOM_VALUE, zoomOptions = DEFAULT_ZOOM_OPTIONS, onOpen, onDownload, onPrint, onRetry, labels, }: PDFViewerProps) { const viewportRef = useRef(null); const [fitZoom, setFitZoom] = useState(100); const [zoom, setZoom] = useState(() => defaultZoom === FIT_ZOOM_VALUE ? FIT_ZOOM_VALUE : String(clampZoom(defaultZoom, zoomOptions)) ); const minZoom = Math.min(...zoomOptions); const maxZoom = Math.max(...zoomOptions); const resolvedLabels: PDFViewerLabels = { zoomOut: "Alejar", zoomIn: "Acercar", zoomSelect: "Seleccionar zoom", fit: "Ajustar", open: "Abrir PDF", download: "Descargar PDF", print: "Imprimir PDF", loading: "Cargando vista previa del PDF...", refreshing: "Actualizando documento...", errorTitle: "No se pudo cargar la vista previa", retry: "Reintentar", emptyTitle: "Vista previa no disponible", emptyDescription: "El PDF del documento todavía no está listo para mostrarse.", ...labels, }; const resolvedSrc = useMemo(() => { if (!src) { return null; } if (variant === "native-inline") { return src; } const separator = src.includes("#") ? "&" : "#"; return `${src}${separator}toolbar=0&navpanes=0&scrollbar=1&page=1&zoom=page-width`; }, [src, variant]); useEffect(() => { if (zoom !== FIT_ZOOM_VALUE) { return; } const element = viewportRef.current; if (!element) { return; } const updateFitZoom = () => { const styles = window.getComputedStyle(element); const paddingX = Number.parseFloat(styles.paddingLeft) + Number.parseFloat(styles.paddingRight); const availableWidth = Math.max(element.clientWidth - paddingX, 320); const widthScale = availableWidth / PDF_PAGE_WIDTH; const nextScale = Math.min(widthScale, maxZoom / 100); setFitZoom(Math.max(Math.round(nextScale * 100), 25)); }; updateFitZoom(); const observer = new ResizeObserver(() => updateFitZoom()); observer.observe(element); window.addEventListener("resize", updateFitZoom); return () => { observer.disconnect(); window.removeEventListener("resize", updateFitZoom); }; }, [maxZoom, zoom]); const effectiveZoom = zoom === FIT_ZOOM_VALUE ? fitZoom : Number(zoom); const zoomScale = effectiveZoom / 100; const scaledPageHeight = PDF_PAGE_HEIGHT * zoomScale; const handleOpen = () => { if (onOpen) { onOpen(); return; } if (src) { window.open(src, "_blank", "noopener,noreferrer"); } }; const handleDownload = () => { if (onDownload) { onDownload(); return; } if (!src) { return; } const link = document.createElement("a"); link.href = src; link.download = ""; link.click(); }; const handlePrint = () => { if (onPrint) { onPrint(); return; } if (src) { window.open(src, "_blank", "noopener,noreferrer"); } }; const viewerContent = (
{loading ? (
{resolvedLabels.loading}
) : null} {!loading && error ? (
{resolvedLabels.errorTitle}

{error}

{onRetry ? ( ) : null}
) : null} {!(loading || error) && resolvedSrc ? (
{variant === "native-inline" ? (