Refactor document job download functionality and enhance error handling

- Updated the download functionality in NewDocumentJob to use a unified downloadTemplate method, supporting multiple file formats (PDF, PNG, JPEG, SVG).
- Improved error handling in ApiServerContext by adding getTemplateErrorFromResponse and triggerFileDownload functions for better response management.
- Refactored fetchTemplatePDF to utilize fetchTemplateDownload for streamlined code and improved maintainability.
- Cleaned up ObjectDisplay component by removing unnecessary line breaks for better readability.
This commit is contained in:
Tom Butcher 2026-08-19 18:18:33 +01:00
parent b121179322
commit 3b69be7584
3 changed files with 196 additions and 51 deletions

View File

@ -8,7 +8,7 @@ import { useContext, useRef, useState } from 'react'
import dayjs from 'dayjs' import dayjs from 'dayjs'
const NewDocumentJob = ({ onOk, defaultValues = {} }) => { const NewDocumentJob = ({ onOk, defaultValues = {} }) => {
const { sendObjectAction, downloadTemplatePDF, formatFileName } = const { sendObjectAction, downloadTemplate, formatFileName } =
useContext(ApiServerContext) useContext(ApiServerContext)
const [downloading, setDownloading] = useState(false) const [downloading, setDownloading] = useState(false)
@ -92,11 +92,12 @@ const NewDocumentJob = ({ onOk, defaultValues = {} }) => {
key: 'pdf', key: 'pdf',
onClick: () => { onClick: () => {
setDownloading(true) setDownloading(true)
downloadTemplatePDF( downloadTemplate(
objectData.documentTemplate._id, objectData.documentTemplate._id,
objectData.documentTemplate.content, objectData.documentTemplate.content,
objectData.object, objectData.object,
fileName, fileName,
'pdf',
() => { () => {
setDownloading(false) setDownloading(false)
} }
@ -105,11 +106,54 @@ const NewDocumentJob = ({ onOk, defaultValues = {} }) => {
}, },
{ {
label: 'PNG', label: 'PNG',
key: 'png' key: 'png',
onClick: () => {
setDownloading(true)
downloadTemplate(
objectData.documentTemplate._id,
objectData.documentTemplate.content,
objectData.object,
fileName,
'png',
() => {
setDownloading(false)
}
)
}
}, },
{ {
label: 'JPEG', label: 'JPEG',
key: 'jpeg' key: 'jpeg',
onClick: () => {
setDownloading(true)
downloadTemplate(
objectData.documentTemplate._id,
objectData.documentTemplate.content,
objectData.object,
fileName,
'jpeg',
() => {
setDownloading(false)
}
)
}
},
{
label: 'SVG',
key: 'svg',
onClick: () => {
setDownloading(true)
downloadTemplate(
objectData.documentTemplate._id,
objectData.documentTemplate.content,
objectData.object,
fileName,
'svg',
() => {
setDownloading(false)
}
)
}
} }
] ]
} }

View File

@ -192,9 +192,7 @@ const ObjectDisplay = ({
if (!objectData?.name) return null if (!objectData?.name) return null
const textElement = ( const textElement = (
<ElipsisText style={{ lineHeight: '1', paddingBottom: '2px' }}> <ElipsisText style={{ lineHeight: '1' }}>{objectData.name}</ElipsisText>
{objectData.name}
</ElipsisText>
) )
// If hyperlink is enabled // If hyperlink is enabled

View File

@ -1750,6 +1750,30 @@ const ApiServerProvider = ({ children }) => {
} }
} }
const getTemplateErrorFromResponse = (err) => {
const data = err?.response?.data
if (data instanceof ArrayBuffer) {
try {
const parsed = JSON.parse(new TextDecoder().decode(data))
return parsed?.error || err.message
} catch {
return err.message
}
}
return data?.error || err.message
}
const triggerFileDownload = (blob, filename) => {
const fileUrl = URL.createObjectURL(blob)
const fileLink = document.createElement('a')
fileLink.href = fileUrl
fileLink.setAttribute('download', filename)
document.body.appendChild(fileLink)
fileLink.click()
fileLink.parentNode.removeChild(fileLink)
URL.revokeObjectURL(fileUrl)
}
const fetchTemplatePreview = async ( const fetchTemplatePreview = async (
id, id,
content, content,
@ -1758,38 +1782,138 @@ const ApiServerProvider = ({ children }) => {
callback callback
) => { ) => {
logger.debug('Fetching preview...') logger.debug('Fetching preview...')
if (socketRef.current && socketRef.current.connected) { try {
return socketRef.current.emit( const response = await axios.post(
'previewTemplate', `${config.backendUrl}/documenttemplates/${id}/preview`,
{ {
_id: id, content,
content: content, testObject,
testObject: testObject, scale
scale: scale
}, },
callback {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${token}`
}
}
) )
if (typeof callback === 'function') {
callback(response.data)
}
return response.data
} catch (err) {
const error = getTemplateErrorFromResponse(err)
logger.error('Error fetching template preview:', error)
if (typeof callback === 'function') {
callback({ error })
}
return { error }
} }
if (typeof callback === 'function') { }
callback({ error: 'Api Server disconnected' })
const fetchTemplateDownload = async (id, content, object, type = 'pdf') => {
logger.debug(`Fetching template download as ${type}...`)
const response = await axios.post(
`${config.backendUrl}/documenttemplates/${id}/download`,
{
content,
object
},
{
params: { type },
responseType: 'arraybuffer',
headers: {
Authorization: `Bearer ${token}`
}
}
)
const contentType = response.headers['content-type'] || ''
if (contentType.includes('application/json')) {
return JSON.parse(new TextDecoder().decode(response.data))
}
return {
type,
mime: contentType,
buffer: response.data
} }
} }
const fetchTemplatePDF = async (id, content, testObject, callback) => { const fetchTemplatePDF = async (id, content, testObject, callback) => {
logger.debug('Fetching pdf template...') logger.debug('Fetching pdf template...')
if (socketRef.current && socketRef.current.connected) { try {
return socketRef.current.emit( const result = await fetchTemplateDownload(
'renderTemplatePDF', id,
{ content,
_id: id, testObject,
content: content, 'pdf'
object: testObject
},
callback
) )
const payload = result.error
? result
: { pdf: result.buffer || result }
if (typeof callback === 'function') {
callback(payload)
}
return payload
} catch (err) {
const error = getTemplateErrorFromResponse(err)
logger.error('Error fetching template PDF:', error)
if (typeof callback === 'function') {
callback({ error })
}
return { error }
} }
if (typeof callback === 'function') { }
callback({ error: 'Api Server disconnected' })
const downloadTemplate = async (
id,
content,
object,
filename,
type = 'pdf',
callback
) => {
logger.debug(`Downloading template as ${type}...`)
try {
const result = await fetchTemplateDownload(id, content, object, type)
if (result?.error) {
if (callback) {
callback(result.error)
}
return result
}
if (Array.isArray(result.images)) {
result.images.forEach((image, index) => {
const binary = atob(image)
const bytes = new Uint8Array(binary.length)
for (let i = 0; i < binary.length; i += 1) {
bytes[i] = binary.charCodeAt(i)
}
const blob = new Blob([bytes], { type: result.mime })
const suffix = result.images.length > 1 ? `-${index + 1}` : ''
triggerFileDownload(
blob,
`${filename}${suffix}.${result.extension || type}`
)
})
} else {
const blob = new Blob([result.buffer], {
type: result.mime || 'application/octet-stream'
})
const extension = type === 'jpeg' ? 'jpeg' : type
triggerFileDownload(blob, `${filename}.${extension}`)
}
if (callback) {
callback()
}
} catch (err) {
const error = getTemplateErrorFromResponse(err)
logger.error('Error downloading template:', error)
if (callback) {
callback(error)
}
return { error }
} }
} }
@ -1800,29 +1924,7 @@ const ApiServerProvider = ({ children }) => {
filename, filename,
callback callback
) => { ) => {
logger.debug('Downloading template PDF...') return downloadTemplate(id, content, object, filename, 'pdf', callback)
fetchTemplatePDF(id, content, object, (result) => {
logger.debug('Downloading template PDF result:', result)
if (result?.error) {
console.error(result.error)
if (callback) {
callback(result.error)
}
} else {
const pdfBlob = new Blob([result.pdf], { type: 'application/pdf' })
const pdfUrl = URL.createObjectURL(pdfBlob)
const fileLink = document.createElement('a')
fileLink.href = pdfUrl
fileLink.setAttribute('download', `${filename}.pdf`)
document.body.appendChild(fileLink)
fileLink.click()
fileLink.parentNode.removeChild(fileLink)
if (callback) {
callback()
}
}
})
} }
const fetchHostOTP = async (id, callback) => { const fetchHostOTP = async (id, callback) => {
@ -2253,6 +2355,7 @@ const ApiServerProvider = ({ children }) => {
fetchTemplatePreview, fetchTemplatePreview,
fetchTemplatePDF, fetchTemplatePDF,
fetchNotes, fetchNotes,
downloadTemplate,
downloadTemplatePDF, downloadTemplatePDF,
fetchHostOTP, fetchHostOTP,
sendObjectAction, sendObjectAction,