mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-28 23:00:59 +00:00
feat: integrate PdfPreviewModal across apps
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { notifications } from '@mantine/notifications';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { extractErrorMessage } from '@ema-platform/api';
|
||||
@@ -13,12 +13,14 @@ interface PreviewArgs {
|
||||
|
||||
/**
|
||||
* Renders the editor's current contents, not the saved row, so unsaved edits
|
||||
* are what you see. Opened as a blob so it never leaves a file behind.
|
||||
* are what you see. Opened as a blob into `PdfPreviewModal` rather than a new
|
||||
* tab, so the designer never loses their place.
|
||||
*/
|
||||
export function useTemplatePreview() {
|
||||
const { t } = useTranslation();
|
||||
const [previewUrl, setPreviewUrl] = useState<string | null>(null);
|
||||
|
||||
return useCallback(
|
||||
const open = useCallback(
|
||||
async ({ hbsSource, licenseTypeId, landscape }: PreviewArgs) => {
|
||||
try {
|
||||
// The preview returns a PDF stream, not JSON, so it bypasses RTK Query
|
||||
@@ -38,10 +40,7 @@ export function useTemplatePreview() {
|
||||
}),
|
||||
});
|
||||
if (!response.ok) throw new Error(await response.text());
|
||||
const url = URL.createObjectURL(await response.blob());
|
||||
window.open(url, '_blank', 'noopener');
|
||||
// Give the new tab time to read it before revoking.
|
||||
setTimeout(() => URL.revokeObjectURL(url), 60_000);
|
||||
setPreviewUrl(URL.createObjectURL(await response.blob()));
|
||||
} catch (err) {
|
||||
notifications.show({
|
||||
color: 'red',
|
||||
@@ -52,4 +51,13 @@ export function useTemplatePreview() {
|
||||
},
|
||||
[t],
|
||||
);
|
||||
|
||||
const close = useCallback(() => {
|
||||
setPreviewUrl((current) => {
|
||||
if (current) URL.revokeObjectURL(current);
|
||||
return null;
|
||||
});
|
||||
}, []);
|
||||
|
||||
return { previewUrl, open, close };
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
useUpdateLicenseValidityMutation,
|
||||
useUpdateLicenseTemplateMutation,
|
||||
} from '@ema-platform/api';
|
||||
import { EmptyState, ErrorState, PageHeader } from '@ema-platform/ui';
|
||||
import { EmptyState, ErrorState, PageHeader, PdfPreviewModal } from '@ema-platform/ui';
|
||||
import { usePermissions, LICENSE_PERMISSIONS as PERMISSIONS } from '@ema-platform/auth';
|
||||
import { BlockPropertiesPanel } from '../components/BlockPropertiesPanel';
|
||||
import { DesignerToolbar } from '../components/DesignerToolbar';
|
||||
@@ -86,7 +86,7 @@ export function CertificateDesignerPage() {
|
||||
|
||||
const draft = useTemplateDraft(templates);
|
||||
const run = useDesignerActions();
|
||||
const openPreview = useTemplatePreview();
|
||||
const { previewUrl, open: openPreview, close: closePreview } = useTemplatePreview();
|
||||
|
||||
const [newOpen, setNewOpen] = useState(false);
|
||||
const [newName, setNewName] = useState('');
|
||||
@@ -377,6 +377,13 @@ export function CertificateDesignerPage() {
|
||||
}, t('designer.created', 'Draft created'))
|
||||
}
|
||||
/>
|
||||
|
||||
<PdfPreviewModal
|
||||
opened={Boolean(previewUrl)}
|
||||
onClose={closePreview}
|
||||
url={previewUrl ?? ''}
|
||||
title={t('designer.preview', 'Preview')}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,12 @@ import {
|
||||
IconPaperclip,
|
||||
IconStethoscope,
|
||||
} from '@tabler/icons-react';
|
||||
import { AdvancedTable, notify, type AdvancedColumn } from '@ema-platform/ui';
|
||||
import {
|
||||
AdvancedTable,
|
||||
notify,
|
||||
PdfPreviewModal,
|
||||
type AdvancedColumn,
|
||||
} from '@ema-platform/ui';
|
||||
import { useDateDisplayer } from '@ema-platform/shared';
|
||||
import {
|
||||
extractErrorMessage,
|
||||
@@ -109,6 +114,9 @@ function AttachmentsModal({
|
||||
{ ownerType, ownerId: ownerId ?? '' },
|
||||
{ skip: !ownerId },
|
||||
);
|
||||
const [preview, setPreview] = useState<{ url: string; title: string } | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
const files = useMemo(
|
||||
() => (attachments ?? []).flatMap((a) => a.files ?? []),
|
||||
@@ -156,10 +164,9 @@ function AttachmentsModal({
|
||||
size="compact-xs"
|
||||
variant="light"
|
||||
leftSection={<IconEye size={14} />}
|
||||
component="a"
|
||||
href={file.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={() =>
|
||||
setPreview({ url: file.url as string, title: file.originalName })
|
||||
}
|
||||
>
|
||||
{t('recordVerification.view', 'View')}
|
||||
</Button>
|
||||
@@ -173,6 +180,12 @@ function AttachmentsModal({
|
||||
))
|
||||
)}
|
||||
</Stack>
|
||||
<PdfPreviewModal
|
||||
opened={Boolean(preview)}
|
||||
onClose={() => setPreview(null)}
|
||||
url={preview?.url ?? ''}
|
||||
title={preview?.title}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user