diff --git a/ui/src/App.tsx b/ui/src/App.tsx index 2ad7adb..1277127 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -1,6 +1,6 @@ import { Routes, Route, useNavigate } from 'react-router'; import UploadPage from './pages/UploadPage'; -import VerifyPage from './pages/VerifyPage'; +import VerifyPage from './pages/VerifyPage/VerifyPage'; import DownloadPage from './pages/DownloadPage'; import NotSignedInPage from './pages/NotSignedInPage'; import SignInPage from './pages/SignInPage'; diff --git a/ui/src/pages/VerifyPage.tsx b/ui/src/pages/VerifyPage/VerifyPage.tsx similarity index 55% rename from ui/src/pages/VerifyPage.tsx rename to ui/src/pages/VerifyPage/VerifyPage.tsx index 63a91eb..f7a4ba4 100644 --- a/ui/src/pages/VerifyPage.tsx +++ b/ui/src/pages/VerifyPage/VerifyPage.tsx @@ -1,214 +1,33 @@ -import React, { useEffect, useState } from 'react'; -import Layout from '../components/Layout'; -import { - authorizedFetch, - FieldData, - GetDocumentResponse, - UpdateDocumentResponse, -} from '../utils/api'; -import { useNavigate } from 'react-router'; -import { shouldUseTextarea } from '../utils/formUtils'; +import Layout from '../../components/Layout'; +import { ExtractedData } from '../../utils/api'; +import { shouldUseTextarea } from '../../utils/formUtils'; +import { useVerifyPage } from './useVerifyPage.ts'; interface VerifyPageProps { signOut: () => Promise; } export default function VerifyPage({ signOut }: VerifyPageProps) { - const [documentId] = useState(() => - sessionStorage.getItem('documentId') - ); - const [responseData, setResponseData] = useState( - null - ); - const [loading, setLoading] = useState(true); // tracks if the page is loading - const [error, setError] = useState(false); // tracks when there is an error - - const navigate = useNavigate(); - - async function pollApiRequest(attempts = 30, delay = 2000) { - // Helper function to sleep for the specified delay - const sleep = () => new Promise((resolve) => setTimeout(resolve, delay)); - - if (!documentId) { - console.error('No documentId available for API request'); - setLoading(false); - setError(true); - return; - } - - for (let i = 0; i < attempts; i++) { - try { - const response = await authorizedFetch(`/api/document/${documentId}`, { - method: 'GET', - headers: { - Accept: 'application/json', - }, - }); - - if (response.status === 401 || response.status === 403) { - alert('You are no longer signed in! Please sign in again.'); - signOut(); - return; - } else if (!response.ok) { - console.warn(`Attempt ${i + 1} failed: ${response.statusText}`); - await sleep(); - continue; - } - - const result = (await response.json()) as GetDocumentResponse; // parse response - - if (result.status !== 'complete') { - console.info( - `Attempt ${i + 1} is not complete. Trying again in a little bit.` - ); - await sleep(); - continue; - } - - setResponseData(result); // store API data in state - setLoading(false); // stop loading when data is received - setError(false); // clear any previous errors - return; - } catch (error) { - console.error(`Attempt ${i + 1} failed:`, error); - await sleep(); - } - } - - console.error('Attempt failed after max attempts'); - setLoading(false); - setError(true); - } - - async function handleVerifySubmit(event: React.FormEvent) { - event.preventDefault(); - - if (!responseData || !responseData.extracted_data) { - console.log('no extracted data available'); - return; - } - - const formData = { - extracted_data: responseData.extracted_data, - }; - - try { - const apiUrl = `/api/document/${responseData.document_id}`; - const response = await authorizedFetch(apiUrl, { - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(formData), - }); - - if (response.ok) { - const result = (await response.json()) as UpdateDocumentResponse; - sessionStorage.setItem('verifiedData', JSON.stringify(result)); - navigate('/download-document'); - alert('Data saved successfully!'); - } else if (response.status === 401 || response.status === 403) { - alert('You are no longer signed in! Please sign in again.'); - signOut(); - } else { - const result = await response.json(); - alert('Failed to save data: ' + result.error); - } - } catch (error) { - console.error('Error submitting data:', error); - alert('An error occurred while saving.'); - } - } - - useEffect(() => { - if (!documentId) { - console.error('No documentId found in sessionStorage'); - setLoading(false); - setError(true); - return; - } - pollApiRequest(); - }, []); // runs only once when the component mounts - - function displayFileName(): string { - return responseData?.document_key - ? responseData?.document_key.replace('input/', '') - : ' '; - } - - function handleInputChange( - event: React.ChangeEvent, - key: string, - field: FieldData + const { + getDocumentResponseData, + loading, + error, + handleVerifySubmit, + handleInputChange, + displayFileName, + } = useVerifyPage(signOut); + + function displayFilePreview( + base64_encoded_file: string, + document_key?: string ) { - setResponseData((prevData) => { - if (!prevData) return null; - - return { - ...prevData, // keep previous data - extracted_data: { - ...prevData.extracted_data, // keep other fields the same - [key]: { ...field, value: event.target.value }, - }, - }; - }); - } - - function displayExtractedData() { - if (!responseData?.extracted_data) { - console.warn('No extracted data found.'); - return; - } - - return Object.entries(responseData.extracted_data) - .sort(([keyA], [keyB]) => - keyA.localeCompare(keyB, undefined, { numeric: true }) - ) - .map(([key, field]) => { - return ( -
- - {shouldUseTextarea(field.value) ? ( -