1- import { RefObject } from "react" ;
1+ import { RefObject , useCallback } from "react" ;
22import { EmbedPDF } from "@simplepdf/react-embed-pdf" ;
33
44interface ProcessingStepsProps {
@@ -11,62 +11,75 @@ interface ProcessingStepsProps {
1111 onDetect : ( ) => void ;
1212}
1313
14- export function ProcessingSteps ( {
14+ const EXAMPLE_PDF_URL =
15+ "https://us-beautiful-space.nyc3.digitaloceanspaces.com/commonforms/cerfa_14571-05_LONG_SEJOUR_EN.pdf" ;
16+ const EXAMPLE_PDF_FILENAME = "cerfa_14571-05_LONG_SEJOUR_EN.pdf" ;
17+
18+ export const ProcessingSteps = ( {
1519 pdfFile,
1620 isProcessing,
1721 hasResult,
1822 pdfWithAcroFieldsBlobUrl,
1923 fileInputRef,
2024 onFileSelect,
2125 onDetect,
22- } : ProcessingStepsProps ) {
23- const handleDownload = ( ) => {
24- if ( ! pdfWithAcroFieldsBlobUrl || ! pdfFile ) {
26+ } : ProcessingStepsProps ) => {
27+ const handleDownload = useCallback ( ( ) => {
28+ const canDownload = pdfWithAcroFieldsBlobUrl && pdfFile ;
29+ if ( ! canDownload ) {
2530 return ;
2631 }
2732
28- const link = document . createElement ( "a" ) ;
29- link . href = pdfWithAcroFieldsBlobUrl ;
30- link . download = pdfFile . name . replace ( ".pdf" , "_with_fields.pdf" ) ;
31- document . body . appendChild ( link ) ;
32- link . click ( ) ;
33- document . body . removeChild ( link ) ;
34- } ;
33+ const downloadLink = document . createElement ( "a" ) ;
34+ downloadLink . href = pdfWithAcroFieldsBlobUrl ;
35+ downloadLink . download = pdfFile . name . replace ( ".pdf" , "_with_fields.pdf" ) ;
36+ document . body . appendChild ( downloadLink ) ;
37+ downloadLink . click ( ) ;
38+ document . body . removeChild ( downloadLink ) ;
39+ } , [ pdfWithAcroFieldsBlobUrl , pdfFile ] ) ;
3540
36- const handleLoadExample = async ( ) => {
41+ const handleLoadExample = useCallback ( async ( ) => {
3742 try {
38- const response = await fetch (
39- "https://us-beautiful-space.nyc3.digitaloceanspaces.com/commonforms/cerfa_14571-05_LONG_SEJOUR_EN.pdf"
40- ) ;
43+ const fetchResponse = await fetch ( EXAMPLE_PDF_URL ) ;
4144
42- if ( ! response . ok ) {
43- console . error ( "Failed to fetch example PDF:" , response . statusText ) ;
45+ if ( ! fetchResponse . ok ) {
46+ console . error ( "Failed to fetch example PDF:" , fetchResponse . statusText ) ;
4447 return ;
4548 }
4649
47- const blob = await response . blob ( ) ;
48- const file = new File ( [ blob ] , "cerfa_14571-05_LONG_SEJOUR_EN.pdf" , {
50+ const pdfBlob = await fetchResponse . blob ( ) ;
51+ const exampleFile = new File ( [ pdfBlob ] , EXAMPLE_PDF_FILENAME , {
4952 type : "application/pdf" ,
5053 } ) ;
5154
5255 const dataTransfer = new DataTransfer ( ) ;
53- dataTransfer . items . add ( file ) ;
56+ dataTransfer . items . add ( exampleFile ) ;
5457
55- if ( fileInputRef . current ) {
56- fileInputRef . current . files = dataTransfer . files ;
58+ if ( ! fileInputRef . current ) {
59+ return ;
60+ }
5761
58- const event = new Event ( "change" , { bubbles : true } ) ;
59- Object . defineProperty ( event , "target" , {
60- writable : false ,
61- value : fileInputRef . current ,
62- } ) ;
62+ fileInputRef . current . files = dataTransfer . files ;
6363
64- onFileSelect ( event as unknown as React . ChangeEvent < HTMLInputElement > ) ;
65- }
64+ const changeEvent = new Event ( "change" , { bubbles : true } ) ;
65+ Object . defineProperty ( changeEvent , "target" , {
66+ writable : false ,
67+ value : fileInputRef . current ,
68+ } ) ;
69+
70+ onFileSelect ( changeEvent as unknown as React . ChangeEvent < HTMLInputElement > ) ;
6671 } catch ( error ) {
6772 console . error ( "Failed to load example PDF:" , error ) ;
6873 }
69- } ;
74+ } , [ fileInputRef , onFileSelect ] ) ;
75+
76+ const handleFileInputClick = useCallback ( ( ) => {
77+ fileInputRef . current ?. click ( ) ;
78+ } , [ fileInputRef ] ) ;
79+
80+ const isDetectDisabled = ! pdfFile || isProcessing ;
81+ const isResultDisabled = ! hasResult || isProcessing ;
82+
7083 return (
7184 < >
7285 < div className = "mb-6 md:mb-8 grid grid-cols-1 md:grid-cols-3 gap-4 md:gap-6" >
@@ -87,7 +100,7 @@ export function ProcessingSteps({
87100 />
88101 < div className = "flex flex-col gap-2" >
89102 < button
90- onClick = { ( ) => fileInputRef . current ?. click ( ) }
103+ onClick = { handleFileInputClick }
91104 className = "px-4 py-2 bg-sky-500 text-white rounded-lg hover:bg-sky-600 transition-colors font-medium text-sm cursor-pointer overflow-hidden text-ellipsis whitespace-nowrap"
92105 >
93106 { pdfFile ? `Selected: ${ pdfFile . name } ` : "Choose PDF Form" }
@@ -112,9 +125,9 @@ export function ProcessingSteps({
112125 < div className = "border border-gray-300 rounded-lg p-4 md:p-6 text-center flex flex-col justify-start gap-2 md:gap-3 h-32" >
113126 < button
114127 onClick = { onDetect }
115- disabled = { ! pdfFile || isProcessing }
128+ disabled = { isDetectDisabled }
116129 className = { `px-4 py-2 rounded-lg font-medium text-sm transition-colors ${
117- ! pdfFile || isProcessing
130+ isDetectDisabled
118131 ? "bg-gray-300 text-gray-500 cursor-not-allowed"
119132 : "bg-emerald-500 hover:bg-emerald-600 text-white cursor-pointer"
120133 } `}
@@ -136,7 +149,7 @@ export function ProcessingSteps({
136149 < a
137150 href = { pdfWithAcroFieldsBlobUrl ?? "" }
138151 className = { `inline-block px-4 py-2 rounded-lg font-medium text-sm transition-colors ${
139- ! hasResult || isProcessing
152+ isResultDisabled
140153 ? "bg-gray-300 text-gray-500 cursor-not-allowed pointer-events-none"
141154 : "bg-indigo-600 hover:bg-indigo-700 text-white cursor-pointer"
142155 } `}
@@ -146,9 +159,9 @@ export function ProcessingSteps({
146159 </ EmbedPDF >
147160 < button
148161 onClick = { handleDownload }
149- disabled = { ! hasResult || isProcessing }
162+ disabled = { isResultDisabled }
150163 className = { `px-4 py-2 rounded-lg font-medium text-sm transition-colors ${
151- ! hasResult || isProcessing
164+ isResultDisabled
152165 ? "bg-gray-300 text-gray-500 cursor-not-allowed"
153166 : "bg-cyan-800 hover:bg-cyan-900 text-white cursor-pointer"
154167 } `}
0 commit comments