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