import React, { useState, useEffect, useCallback } from 'react' import ActionCancelModalComponent from './ActionCancelModalComponent' import { defaultOptions, PdfExportOptions } from './PdfExportOptions' import { PdfExportOptionsForm } from './PdfExportOptionsForm' import { usePdfExportJob } from './usePdfExportJob' interface ExportAsPDFModalProps { isOpen: boolean onClose: () => void initialOptions?: Partial jobCreationUrl: string // Full URL to POST to for creating the job /** Returns extra fields for the job creation request body, such as print state too large for * the URL. Called when the export starts. */ getJobCreationBody?: () => { [key: string]: any } apiUrl: string onOptionsChange?: (options: PdfExportOptions) => void // Callback when options change /** Renders additional options specific to what is being exported */ renderExtraOptions?: (disabled: boolean) => React.ReactNode } /** * Modal for exporting a page as a PDF using job polling to handle the PDF generation. * Composes PdfExportOptionsForm and usePdfExportJob for a complete export experience. */ export function ExportAsPDFModal(props: ExportAsPDFModalProps) { const { isOpen, onClose, initialOptions, jobCreationUrl, apiUrl } = props const [options, setOptions] = useState({ ...defaultOptions, ...initialOptions }) const job = usePdfExportJob({ jobCreationUrl, apiUrl, options, getExtraBody: props.getJobCreationBody, }) // Reset state when modal is opened/closed or props change useEffect(() => { if (isOpen) { setOptions({ ...defaultOptions, ...initialOptions }) job.reset() } }, [isOpen, initialOptions]) // Call onOptionsChange when job starts useEffect(() => { if (job.phase === "creating_job" && props.onOptionsChange) { props.onOptionsChange(options) } }, [job.phase, options, props.onOptionsChange]) const areOptionsDisabled = job.phase === "creating_job" || job.phase === "polling_job" || job.phase === "success" || job.phase === "error" || job.phase === "cancelling" const handleRequestCloseOrCancel = async () => { if (job.phase === "polling_job" || job.phase === "creating_job") { await job.cancelJob() } onClose() } const performDownload = useCallback(() => { if (job.jobId && job.phase === "success") { const downloadUrl = `${apiUrl}print-jobs/${job.jobId}/file` window.location.href = downloadUrl onClose() } }, [job.jobId, job.phase, apiUrl, onClose]) const handlePrimaryAction = () => { if (job.phase === "idle") { job.startJob() } else if (job.phase === "success") { performDownload() } else if (job.phase === "error") { job.startJob() // Retry } } // Effect to auto-download when PDF is ready useEffect(() => { if (job.phase === "success" && job.jobId) { performDownload() } }, [job.phase, job.jobId, performDownload]) if (!isOpen) { return null } const isProcessing = job.phase === "creating_job" || job.phase === "polling_job" || job.phase === "cancelling" let actionLabel = T`Export` if (job.phase === "creating_job") actionLabel = T`Preparing...` else if (job.phase === "polling_job") actionLabel = T`Processing...` else if (job.phase === "success") actionLabel = T`Download PDF` else if (job.phase === "error") actionLabel = T`Try Again` else if (job.phase === "cancelling") actionLabel = T`Cancelling...` let cancelActionLabel = T`Close` if (job.phase === "creating_job" || job.phase === "polling_job" || job.phase === "cancelling") { cancelActionLabel = T`Cancel` } return ( {props.renderExtraOptions?.(areOptionsDisabled)} {job.statusMessage && job.phase === "error" && (
{job.statusMessage}
)}
) }