This commit is contained in:
David Arranz 2026-08-04 17:15:57 +02:00
parent 92b17ba307
commit 32598c174c
3 changed files with 102 additions and 92 deletions

View File

@ -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">
<CardContent className="p-0">
<PDFViewer <PDFViewer
className="h-[calc(100vh-11.5rem)] min-h-[720px]" className="h-[calc(100vh-11.5rem)] min-h-[720px]"
defaultZoom="fit" defaultZoom="fit"
description="Vista previa del PDF real generado para esta proforma."
error={error} error={error}
loading={isLoading} loading={isLoading}
onDownload={onDownload} onDownload={onDownload}
onOpen={onOpen} onOpen={onOpen}
onPrint={onPrint} onPrint={onPrint}
src={src} src={src}
title="Vista previa"
/> />
</CardContent>
</Card>
); );
}; };

View File

@ -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,6 +38,7 @@ export const FormSectionCard = ({
<Card className={className}> <Card className={className}>
{hasHeader && ( {hasHeader && (
<CardHeader className={headerClassName}> <CardHeader className={headerClassName}>
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
<div className="flex items-start gap-2"> <div className="flex items-start gap-2">
{icon ? ( {icon ? (
<div <div
@ -55,6 +59,8 @@ export const FormSectionCard = ({
) : null} ) : null}
</div> </div>
</div> </div>
{actions ? <>{actions}</> : null}
</div>
</CardHeader> </CardHeader>
)} )}

View File

@ -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,9 +230,15 @@ 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"
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 ? ( {loading ? (
<div className="flex h-full min-h-[480px] items-center justify-center rounded-xl border border-dashed bg-background/80"> <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"> <div className="flex items-center gap-3 text-sm text-muted-foreground">
@ -250,7 +260,7 @@ export function PDFViewer({
{!(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",
@ -258,7 +268,7 @@ export function PDFViewer({
}} }}
> >
<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}
@ -278,6 +288,7 @@ export function PDFViewer({
</div> </div>
)} )}
</div> </div>
</div> </FormSectionGrid>
</FormSectionCard>
); );
} }