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'
const NewDocumentJob = ({ onOk, defaultValues = {} }) => {
const { sendObjectAction, downloadTemplatePDF, formatFileName } =
const { sendObjectAction, downloadTemplate, formatFileName } =
useContext(ApiServerContext)
const [downloading, setDownloading] = useState(false)
@ -92,11 +92,12 @@ const NewDocumentJob = ({ onOk, defaultValues = {} }) => {
key: 'pdf',
onClick: () => {
setDownloading(true)
downloadTemplatePDF(
downloadTemplate(
objectData.documentTemplate._id,
objectData.documentTemplate.content,
objectData.object,
fileName,
'pdf',
() => {
setDownloading(false)
}
@ -105,11 +106,54 @@ const NewDocumentJob = ({ onOk, defaultValues = {} }) => {
},
{
label: 'PNG',
key: 'png'
key: 'png',
onClick: () => {
setDownloading(true)
downloadTemplate(
objectData.documentTemplate._id,
objectData.documentTemplate.content,
objectData.object,
fileName,
'png',
() => {
setDownloading(false)
}
)
}
},
{
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
const textElement = (
<ElipsisText style={{ lineHeight: '1', paddingBottom: '2px' }}>
{objectData.name}
</ElipsisText>
<ElipsisText style={{ lineHeight: '1' }}>{objectData.name}</ElipsisText>
)
// 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 (
id,
content,
@ -1758,38 +1782,138 @@ const ApiServerProvider = ({ children }) => {
callback
) => {
logger.debug('Fetching preview...')
if (socketRef.current && socketRef.current.connected) {
return socketRef.current.emit(
'previewTemplate',
try {
const response = await axios.post(
`${config.backendUrl}/documenttemplates/${id}/preview`,
{
_id: id,
content: content,
testObject: testObject,
scale: scale
content,
testObject,
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) => {
logger.debug('Fetching pdf template...')
if (socketRef.current && socketRef.current.connected) {
return socketRef.current.emit(
'renderTemplatePDF',
{
_id: id,
content: content,
object: testObject
},
callback
try {
const result = await fetchTemplateDownload(
id,
content,
testObject,
'pdf'
)
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,
callback
) => {
logger.debug('Downloading template PDF...')
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()
}
}
})
return downloadTemplate(id, content, object, filename, 'pdf', callback)
}
const fetchHostOTP = async (id, callback) => {
@ -2253,6 +2355,7 @@ const ApiServerProvider = ({ children }) => {
fetchTemplatePreview,
fetchTemplatePDF,
fetchNotes,
downloadTemplate,
downloadTemplatePDF,
fetchHostOTP,
sendObjectAction,