.
This commit is contained in:
parent
92b17ba307
commit
32598c174c
@ -1,5 +1,4 @@
|
||||
import { PDFViewer } from "@repo/rdx-ui/components";
|
||||
import { Card, CardContent } from "@repo/shadcn-ui/components";
|
||||
|
||||
type ProformaDetailPreviewCardProps = {
|
||||
src: string | null;
|
||||
@ -19,21 +18,15 @@ export const ProformaDetailPreviewCard = ({
|
||||
onPrint,
|
||||
}: ProformaDetailPreviewCardProps) => {
|
||||
return (
|
||||
<Card className="overflow-hidden border-border/80 shadow-sm">
|
||||
<CardContent className="p-0">
|
||||
<PDFViewer
|
||||
className="h-[calc(100vh-11.5rem)] min-h-[720px]"
|
||||
defaultZoom="fit"
|
||||
description="Vista previa del PDF real generado para esta proforma."
|
||||
error={error}
|
||||
loading={isLoading}
|
||||
onDownload={onDownload}
|
||||
onOpen={onOpen}
|
||||
onPrint={onPrint}
|
||||
src={src}
|
||||
title="Vista previa"
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<PDFViewer
|
||||
className="h-[calc(100vh-11.5rem)] min-h-[720px]"
|
||||
defaultZoom="fit"
|
||||
error={error}
|
||||
loading={isLoading}
|
||||
onDownload={onDownload}
|
||||
onOpen={onOpen}
|
||||
onPrint={onPrint}
|
||||
src={src}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -13,6 +13,7 @@ interface FormSectionCardProps {
|
||||
description?: string;
|
||||
icon?: ReactNode;
|
||||
disabled?: boolean;
|
||||
actions?: ReactNode;
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
headerClassName?: string;
|
||||
@ -24,10 +25,12 @@ export const FormSectionCard = ({
|
||||
description,
|
||||
icon,
|
||||
disabled = false,
|
||||
children,
|
||||
className,
|
||||
headerClassName,
|
||||
contentClassName,
|
||||
actions,
|
||||
|
||||
children,
|
||||
}: FormSectionCardProps) => {
|
||||
const hasHeader = Boolean(title || description || icon);
|
||||
|
||||
@ -35,25 +38,28 @@ export const FormSectionCard = ({
|
||||
<Card className={className}>
|
||||
{hasHeader && (
|
||||
<CardHeader className={headerClassName}>
|
||||
<div className="flex items-start gap-2">
|
||||
{icon ? (
|
||||
<div
|
||||
className={cn(
|
||||
"flex size-6 shrink-0 items-center justify-center",
|
||||
disabled ? "text-muted-foreground" : "text-primary"
|
||||
)}
|
||||
>
|
||||
{" "}
|
||||
{icon}{" "}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="space-y-1">
|
||||
{title ? <CardTitle className="font-semibold">{title}</CardTitle> : null}
|
||||
{description ? (
|
||||
<CardDescription className="font-medium">{description}</CardDescription>
|
||||
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div className="flex items-start gap-2">
|
||||
{icon ? (
|
||||
<div
|
||||
className={cn(
|
||||
"flex size-6 shrink-0 items-center justify-center",
|
||||
disabled ? "text-muted-foreground" : "text-primary"
|
||||
)}
|
||||
>
|
||||
{" "}
|
||||
{icon}{" "}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="space-y-1">
|
||||
{title ? <CardTitle className="font-semibold">{title}</CardTitle> : null}
|
||||
{description ? (
|
||||
<CardDescription className="font-medium">{description}</CardDescription>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
{actions ? <>{actions}</> : null}
|
||||
</div>
|
||||
</CardHeader>
|
||||
)}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { FormSectionCard, FormSectionGrid } from "@repo/rdx-ui/components";
|
||||
import {
|
||||
Button,
|
||||
Select,
|
||||
@ -7,8 +8,14 @@ import {
|
||||
SelectValue,
|
||||
Spinner,
|
||||
} from "@repo/shadcn-ui/components";
|
||||
import { cn } from "@repo/shadcn-ui/lib/utils";
|
||||
import { DownloadIcon, ExternalLinkIcon, PrinterIcon, ZoomInIcon, ZoomOutIcon } from "lucide-react";
|
||||
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];
|
||||
@ -77,8 +84,10 @@ export function PDFViewer({
|
||||
|
||||
const updateFitZoom = () => {
|
||||
const styles = window.getComputedStyle(element);
|
||||
const paddingX = Number.parseFloat(styles.paddingLeft) + Number.parseFloat(styles.paddingRight);
|
||||
const paddingY = Number.parseFloat(styles.paddingTop) + Number.parseFloat(styles.paddingBottom);
|
||||
const paddingX =
|
||||
Number.parseFloat(styles.paddingLeft) + Number.parseFloat(styles.paddingRight);
|
||||
const paddingY =
|
||||
Number.parseFloat(styles.paddingTop) + Number.parseFloat(styles.paddingBottom);
|
||||
const availableWidth = Math.max(element.clientWidth - paddingX, 320);
|
||||
const availableHeight = Math.max(element.clientHeight - paddingY, 320);
|
||||
const widthScale = availableWidth / PDF_PAGE_WIDTH;
|
||||
@ -143,13 +152,8 @@ export function PDFViewer({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn("flex h-full flex-col", className)}>
|
||||
<div className="flex flex-col gap-3 border-b px-5 py-4 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold tracking-tight">{title}</h2>
|
||||
<p className="text-sm text-muted-foreground">{description}</p>
|
||||
</div>
|
||||
|
||||
<FormSectionCard
|
||||
actions={
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<Button
|
||||
disabled={loading || !src || (zoom !== FIT_ZOOM_VALUE && Number(zoom) <= minZoom)}
|
||||
@ -226,58 +230,65 @@ export function PDFViewer({
|
||||
<PrinterIcon className="size-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-auto bg-muted/30 p-4 md:p-5" ref={viewportRef}>
|
||||
{loading ? (
|
||||
<div className="flex h-full min-h-[480px] items-center justify-center rounded-xl border border-dashed bg-background/80">
|
||||
<div className="flex items-center gap-3 text-sm text-muted-foreground">
|
||||
<Spinner className="size-4" />
|
||||
Cargando vista previa del PDF...
|
||||
}
|
||||
className="shadow-sm"
|
||||
contentClassName="space-y-5"
|
||||
description={description}
|
||||
icon={<EyeIcon className="size-5" />}
|
||||
title={title}
|
||||
>
|
||||
<FormSectionGrid className="gap-y-6 md:grid-cols-1">
|
||||
<div className="flex-1 overflow-auto bg-muted p-4 md:p-5" ref={viewportRef}>
|
||||
{loading ? (
|
||||
<div className="flex h-full min-h-[480px] items-center justify-center rounded-xl border border-dashed bg-background/80">
|
||||
<div className="flex items-center gap-3 text-sm text-muted-foreground">
|
||||
<Spinner className="size-4" />
|
||||
Cargando vista previa del PDF...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
) : null}
|
||||
|
||||
{!loading && error ? (
|
||||
<div className="flex h-full min-h-[480px] items-center justify-center rounded-xl border border-dashed bg-background/80 px-6 text-center">
|
||||
<div className="space-y-2">
|
||||
<p className="font-medium text-foreground">No se pudo cargar la vista previa</p>
|
||||
<p className="text-sm text-muted-foreground">{error}</p>
|
||||
{!loading && error ? (
|
||||
<div className="flex h-full min-h-[480px] items-center justify-center rounded-xl border border-dashed bg-background/80 px-6 text-center">
|
||||
<div className="space-y-2">
|
||||
<p className="font-medium text-foreground">No se pudo cargar la vista previa</p>
|
||||
<p className="text-sm text-muted-foreground">{error}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
) : null}
|
||||
|
||||
{!(loading || error) && resolvedSrc ? (
|
||||
<div className="mx-auto flex min-h-full justify-center">
|
||||
<div
|
||||
className="origin-top rounded-xl border bg-background shadow-sm transition-transform"
|
||||
style={{
|
||||
transform: `scale(${zoomScale})`,
|
||||
transformOrigin: "top center",
|
||||
width: `${PDF_PAGE_WIDTH}px`,
|
||||
}}
|
||||
>
|
||||
<iframe
|
||||
className="w-full rounded-xl"
|
||||
src={resolvedSrc}
|
||||
style={{ height: `${PDF_PAGE_HEIGHT}px` }}
|
||||
title={title}
|
||||
/>
|
||||
{!(loading || error) && resolvedSrc ? (
|
||||
<div className="mx-auto flex min-h-full justify-center">
|
||||
<div
|
||||
className="origin-top rounded-xl border bg-red-500 shadow-sm transition-transform"
|
||||
style={{
|
||||
transform: `scale(${zoomScale})`,
|
||||
transformOrigin: "top center",
|
||||
width: `${PDF_PAGE_WIDTH}px`,
|
||||
}}
|
||||
>
|
||||
<iframe
|
||||
className="w-full rounded-xl bg-amber-500"
|
||||
src={resolvedSrc}
|
||||
style={{ height: `${PDF_PAGE_HEIGHT}px` }}
|
||||
title={title}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
) : null}
|
||||
|
||||
{loading || error || resolvedSrc ? null : (
|
||||
<div className="flex h-full min-h-[480px] items-center justify-center rounded-xl border border-dashed bg-background/80 px-6 text-center">
|
||||
<div className="space-y-2">
|
||||
<p className="font-medium text-foreground">Vista previa no disponible</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
El PDF de la proforma todavía no está listo para mostrarse.
|
||||
</p>
|
||||
{loading || error || resolvedSrc ? null : (
|
||||
<div className="flex h-full min-h-[480px] items-center justify-center rounded-xl border border-dashed bg-background/80 px-6 text-center">
|
||||
<div className="space-y-2">
|
||||
<p className="font-medium text-foreground">Vista previa no disponible</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
El PDF de la proforma todavía no está listo para mostrarse.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</FormSectionGrid>
|
||||
</FormSectionCard>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user