From 7d69954322d4642dff4fa2c96069f8936242d65f Mon Sep 17 00:00:00 2001 From: Remi Guan Date: Mon, 30 Dec 2024 20:28:16 +0800 Subject: [PATCH] feat: Back to file button after result generated --- app/actions/runSyncExtractKeyValue.ts | 6 +- .../ExtractKeyValuePairContainer.tsx | 138 ++++++------------ app/hooks/usePlaygroundStore.ts | 4 +- app/types/PlaygroundTypes.ts | 3 +- 4 files changed, 56 insertions(+), 95 deletions(-) diff --git a/app/actions/runSyncExtractKeyValue.ts b/app/actions/runSyncExtractKeyValue.ts index e749438b..5f0183e7 100644 --- a/app/actions/runSyncExtractKeyValue.ts +++ b/app/actions/runSyncExtractKeyValue.ts @@ -30,12 +30,12 @@ export const runSyncExtractKeyValue = async ({ }, }; - const keyValueExtractResponse = await axios.post(extractAPI, params, config); + const extractKeyValueResponse = await axios.post(extractAPI, params, config); - if (keyValueExtractResponse.status !== 200) { + if (extractKeyValueResponse.status !== 200) { throw new Error('Failed to extract key value pairs'); } - const json = keyValueExtractResponse.data.json[0]; + const json = extractKeyValueResponse.data.json[0]; return json; }; diff --git a/app/components/playground/ExtractKeyValuePairContainer.tsx b/app/components/playground/ExtractKeyValuePairContainer.tsx index 967a5946..6dda3a1b 100644 --- a/app/components/playground/ExtractKeyValuePairContainer.tsx +++ b/app/components/playground/ExtractKeyValuePairContainer.tsx @@ -1,12 +1,12 @@ import { toast } from 'react-hot-toast'; -import { useEffect, useState, useMemo } from 'react'; -import { ArrowLeft, Download } from '@phosphor-icons/react'; -import { PlaygroundFile, ExtractState, ProcessType, JobType } from '@/app/types/PlaygroundTypes'; +import { useEffect, useMemo, useState } from 'react'; import { useProductionContext } from './ProductionContext'; +import { ArrowLeft, Download } from '@phosphor-icons/react'; import { uploadFile } from '@/app/actions/uploadFile'; +import { JobParams } from '@/app/actions/apiInterface'; import { runAsyncRequestJob } from '@/app/actions/runAsyncRequestJob'; +import { PlaygroundFile, ExtractState, ProcessType, JobType } from '@/app/types/PlaygroundTypes'; import { runAsyncRequestJob as runPreprodAsyncRequestJob } from '@/app/actions/preprod/runAsyncRequestJob'; -import { JobParams } from '@/app/actions/apiInterface'; import Button from '../Button'; import CodeBlock from '../CodeBlock'; import DocumentViewer from '../DocumentViewer'; @@ -14,31 +14,6 @@ import KeyValueInputs from './KeyValueInputs'; import usePlaygroundStore from '@/app/hooks/usePlaygroundStore'; import ExtractKeyValuePairTutorial from '../tutorials/ExtractKeyValuePairTutorial'; -const isEmptyExtractResult = (result?: string[] | null): boolean => { - if (!result) return true; - if (result.length === 0) return true; - if (result.length === 1 && result[0] === '') return true; - return false; -}; - -const getFileUrl = (file: PlaygroundFile) => { - if (!file.file) return ''; - if (typeof file.file === 'string') return file.file; - return URL.createObjectURL(file.file); -}; - -const shouldShowDocumentViewer = (file: PlaygroundFile | null): boolean => { - return !!file && isEmptyExtractResult(file.extractResult); -}; - -const getDocumentViewerProps = (file: PlaygroundFile | null) => { - if (!file) return null; - return { - fileType: file.file instanceof File ? file.file.type : 'text/plain', - fileUrl: getFileUrl(file) - }; -}; - const downloadExtractedData = (formattedData: string, file?: PlaygroundFile['file']) => { if (!formattedData) return; @@ -56,6 +31,7 @@ const downloadExtractedData = (formattedData: string, file?: PlaygroundFile['fil }; const ExtractKeyValuePairContainer = () => { + const [hideResult, setHideResult] = useState(false); const { apiURL, isProduction } = useProductionContext(); const { selectedFileIndex, files, updateFileAtIndex, token, userId, clientId, addFilesFormData } = usePlaygroundStore(); @@ -63,40 +39,30 @@ const ExtractKeyValuePairContainer = () => { if (selectedFileIndex !== null && files.length > 0) { return files[selectedFileIndex]; } - return null; }, [selectedFileIndex, files]); - // Memoize document viewer state to prevent re-renders when key-value inputs change - const documentViewerState = useMemo(() => { - if (!selectedFile) return null; - - const shouldShow = shouldShowDocumentViewer(selectedFile); - if (!shouldShow) return null; - - return { - shouldShow, - props: getDocumentViewerProps(selectedFile) - }; - }, [selectedFile?.file, selectedFile?.extractResult]); // Only depend on file and extractResult - useEffect(() => { if (!selectedFile) return; - if (selectedFile.keyValueExtractState === ExtractState.EXTRACTING || selectedFile.keyValueExtractState === ExtractState.UPLOADING) { + if (selectedFile.extractKeyValueState === ExtractState.EXTRACTING || selectedFile.extractKeyValueState === ExtractState.UPLOADING) { toast.loading('Extracting data...', { id: 'key-value-extracting-toast' }); } else { toast.dismiss('key-value-extracting-toast'); } - }, [selectedFile?.keyValueExtractState]); + }, [selectedFile?.extractKeyValueState]); const handleSuccess = async (response: any) => { if (!response.data) { toast.error(`${selectedFile?.file instanceof File ? selectedFile.file.name : 'File'}: Received undefined result. Please try again.`); - updateFileAtIndex(selectedFileIndex, 'keyValueExtractState', ExtractState.READY); + updateFileAtIndex(selectedFileIndex, 'extractKeyValueState', ExtractState.READY); return; } - updateFileAtIndex(selectedFileIndex, 'extractResult', response.data); - updateFileAtIndex(selectedFileIndex, 'keyValueExtractState', ExtractState.DONE_EXTRACTING); + + const formattedResult = JSON.stringify(response.data.json[0], null, 2); + + updateFileAtIndex(selectedFileIndex, 'extractKeyValueResult', formattedResult); + updateFileAtIndex(selectedFileIndex, 'extractKeyValueState', ExtractState.DONE_EXTRACTING); + toast.success('Extraction complete!'); }; @@ -104,20 +70,20 @@ const ExtractKeyValuePairContainer = () => { if (error.response) { if (error.response.status === 429) { toast.error('Extract limit reached.'); - updateFileAtIndex(selectedFileIndex, 'keyValueExtractState', ExtractState.LIMIT_REACHED); + updateFileAtIndex(selectedFileIndex, 'extractKeyValueState', ExtractState.LIMIT_REACHED); } else { toast.error('Extraction failed. Please try again.'); - updateFileAtIndex(selectedFileIndex, 'keyValueExtractState', ExtractState.READY); + updateFileAtIndex(selectedFileIndex, 'extractKeyValueState', ExtractState.READY); } } else { toast.error('Error during extraction. Please try again.'); - updateFileAtIndex(selectedFileIndex, 'keyValueExtractState', ExtractState.READY); + updateFileAtIndex(selectedFileIndex, 'extractKeyValueState', ExtractState.READY); } console.error(error); }; const handleTimeout = () => { - updateFileAtIndex(selectedFileIndex, 'keyValueExtractState', ExtractState.READY); + updateFileAtIndex(selectedFileIndex, 'extractKeyValueState', ExtractState.READY); toast.error('Extract request timed out. Please try again.'); }; @@ -132,9 +98,8 @@ const ExtractKeyValuePairContainer = () => { return; } - let loadingToast: string | undefined = undefined; try { - updateFileAtIndex(selectedFileIndex, 'keyValueExtractState', ExtractState.UPLOADING); + updateFileAtIndex(selectedFileIndex, 'extractKeyValueState', ExtractState.UPLOADING); const file = selectedFile.file; const jobParams: JobParams = { @@ -158,12 +123,12 @@ const ExtractKeyValuePairContainer = () => { if (uploadResult instanceof Error) { toast.error('Error uploading file. Please try again.'); - updateFileAtIndex(selectedFileIndex, 'keyValueExtractState', ExtractState.READY); + updateFileAtIndex(selectedFileIndex, 'extractKeyValueState', ExtractState.READY); return; } const fileData = uploadResult.data; - updateFileAtIndex(selectedFileIndex, 'keyValueExtractState', ExtractState.EXTRACTING); + updateFileAtIndex(selectedFileIndex, 'extractKeyValueState', ExtractState.EXTRACTING); // Common job parameters const jobConfig = { @@ -192,61 +157,54 @@ const ExtractKeyValuePairContainer = () => { } catch (error) { toast.error('Extraction failed. Please try again.'); console.error(error); - updateFileAtIndex(selectedFileIndex, 'keyValueExtractState', ExtractState.READY); + updateFileAtIndex(selectedFileIndex, 'extractKeyValueState', ExtractState.READY); } }; - const formattedExtractResult = useMemo(() => { - if (!selectedFile || selectedFile.extractResult === null) return ''; - if (JSON.stringify(selectedFile?.extractResult) === '[""]') return ''; - - try { - const content = typeof selectedFile.extractResult === 'string' - ? JSON.parse(selectedFile.extractResult) - : selectedFile.extractResult; - - // The structure is always { json: [...] } - if (!content.json || !Array.isArray(content.json)) { - console.error('Invalid extract result structure:', content); - return ''; - } - - return JSON.stringify(content.json[0], null, 2); - } catch (error) { - console.error('Error formatting extract result:', error); - return ''; - } - }, [selectedFile?.extractResult]); - + const fileUrl = useMemo(() => { + if (!selectedFile?.file) return ''; + if (typeof selectedFile.file === 'string') return selectedFile.file; + return URL.createObjectURL(selectedFile.file); + }, [selectedFile?.file]); return (
- {documentViewerState?.shouldShow && ( - + {fileUrl && (hideResult || !selectedFile?.extractKeyValueResult) && ( +
+ + {selectedFile?.extractKeyValueResult && ( +
+
+ )} +
)} - {selectedFile?.extractResult && !isEmptyExtractResult(selectedFile?.extractResult) && ( + {!hideResult && selectedFile?.extractKeyValueResult && (
@@ -257,7 +215,7 @@ const ExtractKeyValuePairContainer = () => {
diff --git a/app/hooks/usePlaygroundStore.ts b/app/hooks/usePlaygroundStore.ts index e167ac52..2d61736a 100644 --- a/app/hooks/usePlaygroundStore.ts +++ b/app/hooks/usePlaygroundStore.ts @@ -73,8 +73,9 @@ interface PlaygroundStore { const initialFileState = { extractResult: [''], - qaResult: null, tableExtractResult: [''], + extractKeyValueResult: '', + qaResult: null, tableMdExtractResult: [{ title: '', table: '', tableData: {} }], keyMap: {}, keyValueInputs: [{ key: '', description: '' }], @@ -82,6 +83,7 @@ const initialFileState = { s3_file_source: { s3_bucket: '', source_type: '', s3_prefix: '' }, activeTab: PlaygroundTabs.PLAIN_TEXT, extractState: ExtractState.READY, + extractKeyValueState: ExtractState.READY, extractTab: ExtractTab.INITIAL_STATE, tableTab: TableTab.TABLE_EXTRACT, qaState: TransformState.READY, diff --git a/app/types/PlaygroundTypes.ts b/app/types/PlaygroundTypes.ts index c61df09d..57c3bd25 100644 --- a/app/types/PlaygroundTypes.ts +++ b/app/types/PlaygroundTypes.ts @@ -63,6 +63,7 @@ export interface PlaygroundFile { file: File | string; extractResult: QueryResult; qaResult: QAResult | null; + extractKeyValueResult: string; tableExtractResult: string[]; tableMdExtractResult: ExtractedMDTable[]; tableMappedDataRows: string[][]; @@ -82,7 +83,7 @@ export interface PlaygroundFile { extractState: ExtractState; instructionExtractState: ExtractState; tableMdExtractState: ExtractState; - keyValueExtractState: ExtractState; + extractKeyValueState: ExtractState; extractTab: ExtractTab; tableTab: TableTab; qaState: TransformState;