Auto-format all files modified during the documents-hub-split feature branch that were not yet aligned with the project's Prettier config (single quotes, semicolons, trailing commas). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
104 lines
3.0 KiB
TypeScript
104 lines
3.0 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect, useState } from 'react';
|
|
import { ExternalLink } from 'lucide-react';
|
|
|
|
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
|
import { apiFetch } from '@/lib/api/client';
|
|
|
|
interface FilePreviewDialogProps {
|
|
open: boolean;
|
|
onOpenChange: (open: boolean) => void;
|
|
fileId?: string;
|
|
fileName?: string;
|
|
mimeType?: string;
|
|
}
|
|
|
|
export function FilePreviewDialog({
|
|
open,
|
|
onOpenChange,
|
|
fileId,
|
|
fileName,
|
|
mimeType,
|
|
}: FilePreviewDialogProps) {
|
|
const [previewUrl, setPreviewUrl] = useState<string | null>(null);
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
if (!open || !fileId) {
|
|
setPreviewUrl(null);
|
|
setError(null);
|
|
return;
|
|
}
|
|
|
|
setLoading(true);
|
|
setError(null);
|
|
|
|
apiFetch<{ data: { url: string } }>(`/api/v1/files/${fileId}/preview`)
|
|
.then((res) => {
|
|
setPreviewUrl(res.data.url);
|
|
})
|
|
.catch(() => {
|
|
setError('Failed to load preview');
|
|
})
|
|
.finally(() => {
|
|
setLoading(false);
|
|
});
|
|
}, [open, fileId]);
|
|
|
|
const isImage = mimeType?.startsWith('image/');
|
|
const isPdf = mimeType === 'application/pdf';
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
<DialogContent className="max-w-4xl w-full h-[80vh] flex flex-col">
|
|
<DialogHeader>
|
|
<DialogTitle className="flex items-center gap-2 truncate">
|
|
<span className="truncate">{fileName ?? 'Preview'}</span>
|
|
{previewUrl && (
|
|
<a
|
|
href={previewUrl}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="shrink-0 text-muted-foreground hover:text-foreground"
|
|
>
|
|
<ExternalLink className="h-4 w-4" />
|
|
</a>
|
|
)}
|
|
</DialogTitle>
|
|
</DialogHeader>
|
|
|
|
<div className="flex-1 overflow-hidden rounded-lg border bg-muted/20">
|
|
{loading && (
|
|
<div className="flex h-full items-center justify-center text-sm text-muted-foreground">
|
|
Loading preview...
|
|
</div>
|
|
)}
|
|
|
|
{error && (
|
|
<div className="flex h-full items-center justify-center text-sm text-destructive">
|
|
{error}
|
|
</div>
|
|
)}
|
|
|
|
{!loading && !error && previewUrl && isImage && (
|
|
<div className="flex h-full items-center justify-center p-4">
|
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
|
<img
|
|
src={previewUrl}
|
|
alt={fileName ?? 'Preview'}
|
|
className="max-h-full max-w-full object-contain rounded"
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
{!loading && !error && previewUrl && isPdf && (
|
|
<iframe src={previewUrl} title={fileName ?? 'PDF Preview'} className="h-full w-full" />
|
|
)}
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|