Files
edr-platform/apps/edr-passenger-web/backoffice/src/features/support/useFilePreview.tsx

83 lines
3.0 KiB
TypeScript

'use client';
import { isSupportAttachmentImage } from '@edr/types';
import { Download, ExternalLink, FileText } from 'lucide-react';
import { useCallback, useState } from 'react';
import Modal from '@/components/ui/Modal';
/** The minimal file shape the preview needs. */
export interface PreviewableFile {
name: string;
/**
* A blob object URL, not the attachment's API path — the API guard reads the
* bearer token from the `Authorization` header, which `<img>` and `<a>` can't
* send. Callers fetch the bytes first (see `useAttachmentObjectUrl`).
*/
url: string;
mimeType: string;
}
/**
* Drives a single shared preview modal for the page: call `view(file)` from any
* attachment, render `viewer` once near the page root.
*
* Mirrors the shape of `useFileViewer` from `@edr/ui-common`, which the freight
* backoffice uses. That hook can't be used here: it renders `@mantine/core`
* components, and this app is Tailwind-only with no `MantineProvider` mounted —
* its Modal would throw at runtime. Kept deliberately narrow (images inline,
* everything else handed to the browser) rather than reimplementing the shared
* viewer's pdf/office/video handling; swap this for the shared hook if this app
* ever adopts Mantine.
*/
export function useFilePreview() {
const [file, setFile] = useState<PreviewableFile | null>(null);
const view = useCallback((f: PreviewableFile) => setFile(f), []);
const close = useCallback(() => setFile(null), []);
const viewer = (
<Modal isOpen={file !== null} onClose={close} title={file?.name ?? ''} size="xl">
{file && (
<div className="flex flex-col items-center gap-4">
{isSupportAttachmentImage(file.mimeType) ? (
// eslint-disable-next-line @next/next/no-img-element
<img
src={file.url}
alt={file.name}
className="max-h-[70vh] w-auto max-w-full rounded-lg object-contain"
/>
) : (
<div className="flex flex-col items-center gap-3 py-10 text-muted-foreground">
<FileText size={48} />
<p className="text-sm">No inline preview for this file type.</p>
</div>
)}
<div className="flex gap-2">
<a
href={file.url}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-2 rounded-lg border border-border px-3 py-1.5 text-xs font-medium text-foreground transition hover:bg-muted"
>
<ExternalLink size={14} />
Open in new tab
</a>
<a
href={file.url}
download={file.name}
className="flex items-center gap-2 rounded-lg px-3 py-1.5 text-xs font-medium text-white transition hover:opacity-90"
style={{ background: 'rgb(20 113 76)' }}
>
<Download size={14} />
Download
</a>
</div>
</div>
)}
</Modal>
);
return { view, close, viewer };
}