From e28eb890a5627c915f5b30eaf52b60d7c3dd1ec6 Mon Sep 17 00:00:00 2001 From: halprin Date: Wed, 14 May 2025 14:00:00 -0600 Subject: [PATCH 1/8] Move VerifyPage into sub-folder --- ui/src/App.tsx | 2 +- ui/src/pages/{ => VerifyPage}/VerifyPage.tsx | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) rename ui/src/pages/{ => VerifyPage}/VerifyPage.tsx (98%) 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 98% rename from ui/src/pages/VerifyPage.tsx rename to ui/src/pages/VerifyPage/VerifyPage.tsx index 63a91eb..69bf477 100644 --- a/ui/src/pages/VerifyPage.tsx +++ b/ui/src/pages/VerifyPage/VerifyPage.tsx @@ -1,13 +1,13 @@ import React, { useEffect, useState } from 'react'; -import Layout from '../components/Layout'; +import Layout from '../../components/Layout'; import { authorizedFetch, FieldData, GetDocumentResponse, UpdateDocumentResponse, -} from '../utils/api'; +} from '../../utils/api'; import { useNavigate } from 'react-router'; -import { shouldUseTextarea } from '../utils/formUtils'; +import { shouldUseTextarea } from '../../utils/formUtils'; interface VerifyPageProps { signOut: () => Promise; From d5230b3c8a62bb4927f8d64b4b577364c84fd7d5 Mon Sep 17 00:00:00 2001 From: halprin Date: Wed, 14 May 2025 16:05:02 -0600 Subject: [PATCH 2/8] useVerify --- ui/src/pages/VerifyPage/VerifyPage.tsx | 269 +++++++------------------ ui/src/pages/VerifyPage/useVerify.ts | 191 ++++++++++++++++++ 2 files changed, 259 insertions(+), 201 deletions(-) create mode 100644 ui/src/pages/VerifyPage/useVerify.ts diff --git a/ui/src/pages/VerifyPage/VerifyPage.tsx b/ui/src/pages/VerifyPage/VerifyPage.tsx index 69bf477..79bbc3e 100644 --- a/ui/src/pages/VerifyPage/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 { ExtractedData } from '../../utils/api'; import { shouldUseTextarea } from '../../utils/formUtils'; +import { useVerify } from './useVerify.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 { + responseData, + loading, + error, + handleVerifySubmit, + handleInputChange, + displayFileName, + } = useVerify(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) ? ( -