Added PDF preview option.

This commit is contained in:
Tom Butcher 2025-11-23 13:25:43 +00:00
parent 5cb586246f
commit 8f34c262a0

View File

@ -1,6 +1,6 @@
import { useState, useContext, useEffect, useRef, useCallback } from 'react' import { useState, useContext, useEffect, useRef, useCallback } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { Flex, Button, Input } from 'antd' import { Flex, Button, Input, Select } from 'antd'
import PlusIcon from '../../Icons/PlusIcon.jsx' import PlusIcon from '../../Icons/PlusIcon.jsx'
import MinusIcon from '../../Icons/MinusIcon.jsx' import MinusIcon from '../../Icons/MinusIcon.jsx'
import InfoCircleIcon from '../../Icons/InfoCircleIcon.jsx' import InfoCircleIcon from '../../Icons/InfoCircleIcon.jsx'
@ -14,22 +14,26 @@ const TemplatePreview = ({
isEditing, isEditing,
onTestObjectOpen, onTestObjectOpen,
onPreviewMessage, onPreviewMessage,
showTestObject = false showTestObject = false,
showPreviewSwitch = true
}) => { }) => {
const iframeRef = useRef(null) const iframeRef = useRef(null)
const { fetchTemplatePreview } = useContext(ApiServerContext) const { fetchTemplatePreview, fetchTemplatePDF } =
const [previewContent, setPreviewContent] = useState('') useContext(ApiServerContext)
const [previewContentHTML, setPreviewContentHTML] = useState('')
const [pdfBlob, setPDFBlob] = useState(null)
const [reloadLoading, setReloadLoading] = useState(false) const [reloadLoading, setReloadLoading] = useState(false)
const [previewScale, setPreviewScale] = useState(1) const [previewScale, setPreviewScale] = useState(1)
const [previewType, setPreviewType] = useState('HTML')
const updatePreviewContent = (html) => { const updatePreviewContentHTML = (html) => {
if (iframeRef.current) { if (iframeRef.current) {
// Save current scroll position // Save current scroll position
const scrollY = iframeRef.current.contentWindow.scrollY const scrollY = iframeRef.current.contentWindow.scrollY
const scrollX = iframeRef.current.contentWindow.scrollX const scrollX = iframeRef.current.contentWindow.scrollX
// Update srcDoc // Update srcDoc
setPreviewContent(html) setPreviewContentHTML(html)
// Restore scroll position after iframe loads new content // Restore scroll position after iframe loads new content
const handleLoad = () => { const handleLoad = () => {
@ -40,6 +44,23 @@ const TemplatePreview = ({
} }
} }
const reloadPreviewPDF = (content, testObject = {}) => {
setReloadLoading(true)
fetchTemplatePDF(documentTemplate._id, content, testObject, (result) => {
setReloadLoading(false)
if (result?.error) {
// Handle error through parent component
onPreviewMessage(result.error, true)
} else {
const pdfBlob = new Blob([result.pdf], { type: 'application/pdf' })
const pdfUrl = URL.createObjectURL(pdfBlob)
setPDFBlob(pdfUrl)
onPreviewMessage('No issues found.', false)
}
})
}
const reloadPreview = useCallback( const reloadPreview = useCallback(
(content, testObject = {}, scale = 1) => { (content, testObject = {}, scale = 1) => {
setReloadLoading(true) setReloadLoading(true)
@ -54,7 +75,7 @@ const TemplatePreview = ({
// Handle error through parent component // Handle error through parent component
onPreviewMessage(result.error, true) onPreviewMessage(result.error, true)
} else { } else {
updatePreviewContent(result.html) updatePreviewContentHTML(result.html)
onPreviewMessage('No issues found.', false) onPreviewMessage('No issues found.', false)
} }
} }
@ -66,9 +87,13 @@ const TemplatePreview = ({
// Move useEffect to component level and use state to track objectData changes // Move useEffect to component level and use state to track objectData changes
useEffect(() => { useEffect(() => {
if (documentTemplate?.content) { if (documentTemplate?.content) {
if (previewType == 'HTML') {
reloadPreview(documentTemplate.content, objectData, previewScale) reloadPreview(documentTemplate.content, objectData, previewScale)
} else {
reloadPreviewPDF(documentTemplate.content, objectData)
} }
}, [objectData, documentTemplate, previewScale, reloadPreview]) }
}, [objectData, documentTemplate, previewScale, previewType])
return ( return (
<Flex vertical gap={'middle'} style={{ height: '100%' }}> <Flex vertical gap={'middle'} style={{ height: '100%' }}>
@ -98,34 +123,51 @@ const TemplatePreview = ({
/> />
</> </>
) : null} ) : null}
<Button <Button
icon={<PlusIcon />} icon={<PlusIcon />}
onClick={() => { onClick={() => {
setPreviewScale((prev) => prev + 0.05) setPreviewScale((prev) => prev + 0.05)
}} }}
disabled={loading || reloadLoading || previewType == 'PDF'}
/> />
<Button <Button
icon={<MinusIcon />} icon={<MinusIcon />}
onClick={() => { onClick={() => {
setPreviewScale((prev) => prev - 0.05) setPreviewScale((prev) => prev - 0.05)
}} }}
disabled={loading || reloadLoading || previewType == 'PDF'}
/> />
<Button <Button
readOnly={true} readOnly={true}
style={{ width: '65px' }} style={{ width: '65px' }}
loading={loading || reloadLoading} disabled={loading || reloadLoading || previewType == 'PDF'}
disabled={loading || reloadLoading}
onClick={() => { onClick={() => {
setPreviewScale(1) setPreviewScale(1)
}} }}
> >
{previewScale.toFixed(2)}x {previewScale.toFixed(2)}x
</Button> </Button>
{showPreviewSwitch == true ? (
<Select
options={[
{ value: 'HTML', label: 'HTML' },
{ value: 'PDF', label: 'PDF' }
]}
loading={loading || reloadLoading}
disabled={loading || reloadLoading}
value={previewType}
onChange={(value) => {
setPreviewType(value)
}}
/>
) : null}
</Flex> </Flex>
<iframe <iframe
ref={iframeRef} ref={iframeRef}
srcDoc={previewContent} srcDoc={previewType == 'HTML' ? previewContentHTML : undefined}
src={previewType == 'PDF' ? pdfBlob : undefined}
frameBorder='0' frameBorder='0'
style={{ style={{
width: '100%', width: '100%',
@ -146,7 +188,8 @@ TemplatePreview.propTypes = {
style: PropTypes.object, style: PropTypes.object,
showTestObject: PropTypes.bool, showTestObject: PropTypes.bool,
onTestObjectOpen: PropTypes.func.isRequired, onTestObjectOpen: PropTypes.func.isRequired,
onPreviewMessage: PropTypes.func.isRequired onPreviewMessage: PropTypes.func.isRequired,
showPreviewSwitch: PropTypes.bool
} }
export default TemplatePreview export default TemplatePreview