diff --git a/src/components/run/InterpretationLog.tsx b/src/components/run/InterpretationLog.tsx index fa749efa..69608fb5 100644 --- a/src/components/run/InterpretationLog.tsx +++ b/src/components/run/InterpretationLog.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import SwipeableDrawer from '@mui/material/SwipeableDrawer'; import Typography from '@mui/material/Typography'; -import { Button, Grid } from '@mui/material'; +import { Button, Grid, Tabs, Tab, Box } from '@mui/material'; import { useCallback, useEffect, useRef, useState } from "react"; import { useSocketStore } from "../../context/socket"; import { Buffer } from 'buffer'; @@ -29,9 +29,18 @@ export const InterpretationLog: React.FC = ({ isOpen, se const { t } = useTranslation(); const [log, setLog] = useState(''); const [customValue, setCustomValue] = useState(''); - const [tableData, setTableData] = useState([]); - const [binaryData, setBinaryData] = useState(null); + + const [captureListData, setCaptureListData] = useState([]); + const [captureTextData, setCaptureTextData] = useState([]); + const [screenshotData, setScreenshotData] = useState([]); + const [otherData, setOtherData] = useState([]); + const [captureListPage, setCaptureListPage] = useState(0); + const [screenshotPage, setScreenshotPage] = useState(0); + const [otherPage, setOtherPage] = useState(0); + + const [activeTab, setActiveTab] = useState(0); + const logEndRef = useRef(null); const { browserWidth, outputPreviewHeight, outputPreviewWidth } = useBrowserDimensionsStore(); @@ -62,34 +71,68 @@ export const InterpretationLog: React.FC = ({ isOpen, se setLog((prevState) => prevState + '\n' + `[${new Date().toLocaleString()}] ` + msg); } scrollLogToBottom(); - }, [log, scrollLogToBottom]); + }, []); - const handleSerializableCallback = useCallback((data: any) => { + const handleSerializableCallback = useCallback(({ type, data }: { type: string, data: any }) => { setLog((prevState) => prevState + '\n' + t('interpretation_log.data_sections.serializable_received') + '\n' + JSON.stringify(data, null, 2) + '\n' + t('interpretation_log.data_sections.separator')); - - if (Array.isArray(data)) { - setTableData(data); + + if (type === 'captureList' && Array.isArray(data)) { + setCaptureListData(prev => [...prev, data]); + if (captureListData.length === 0) { + const availableTabs = getAvailableTabs(); + const tabIndex = availableTabs.findIndex(tab => tab.id === 'captureList'); + if (tabIndex !== -1) setActiveTab(tabIndex); + } + } else if (type === 'captureText') { + if (Array.isArray(data)) { + setCaptureTextData(data); + } else { + setCaptureTextData([data]); + } + if (captureTextData.length === 0) { + const availableTabs = getAvailableTabs(); + const tabIndex = availableTabs.findIndex(tab => tab.id === 'captureText'); + if (tabIndex !== -1) setActiveTab(tabIndex); + } + } else if (type === 'other') { + if (Array.isArray(data)) { + setOtherData(prev => [...prev, data]); + } else { + setOtherData(prev => [...prev, [data]]); + } + if (otherData.length === 0) { + const availableTabs = getAvailableTabs(); + const tabIndex = availableTabs.findIndex(tab => tab.id === 'other'); + if (tabIndex !== -1) setActiveTab(tabIndex); + } } - + scrollLogToBottom(); - }, [log, scrollLogToBottom, t]); - - const handleBinaryCallback = useCallback(({ data, mimetype }: any) => { + }, [captureListData.length, captureTextData.length, otherData.length, t]); + + const handleBinaryCallback = useCallback(({ data, mimetype, type }: { data: any, mimetype: string, type: string }) => { const base64String = Buffer.from(data).toString('base64'); const imageSrc = `data:${mimetype};base64,${base64String}`; - + setLog((prevState) => prevState + '\n' + t('interpretation_log.data_sections.binary_received') + '\n' + t('interpretation_log.data_sections.mimetype') + mimetype + '\n' + t('interpretation_log.data_sections.image_below') + '\n' + t('interpretation_log.data_sections.separator')); - - setBinaryData(imageSrc); + + if (type === 'captureScreenshot') { + setScreenshotData(prev => [...prev, imageSrc]); + if (screenshotData.length === 0) { + const availableTabs = getAvailableTabs(); + const tabIndex = availableTabs.findIndex(tab => tab.id === 'captureScreenshot'); + if (tabIndex !== -1) setActiveTab(tabIndex); + } + } + scrollLogToBottom(); - }, [log, scrollLogToBottom, t]); - + }, [screenshotData.length, t]); const handleCustomValueChange = (event: React.ChangeEvent) => { setCustomValue(event.target.value); @@ -98,8 +141,14 @@ export const InterpretationLog: React.FC = ({ isOpen, se useEffect(() => { if (shouldResetInterpretationLog) { setLog(''); - setTableData([]); - setBinaryData(null); + setCaptureListData([]); + setCaptureTextData([]); + setScreenshotData([]); + setOtherData([]); + setActiveTab(0); + setCaptureListPage(0); + setScreenshotPage(0); + setOtherPage(0); } }, [shouldResetInterpretationLog]); @@ -114,10 +163,37 @@ export const InterpretationLog: React.FC = ({ isOpen, se }; }, [socket, handleLog, handleSerializableCallback, handleBinaryCallback]); - // Extract columns dynamically from the first item of tableData - const columns = tableData.length > 0 ? Object.keys(tableData[0]) : []; + const getAvailableTabs = useCallback(() => { + const tabs = []; + + if (captureListData.length > 0) { + tabs.push({ id: 'captureList', label: 'Lists' }); + } + + if (captureTextData.length > 0) { + tabs.push({ id: 'captureText', label: 'Texts' }); + } + + if (screenshotData.length > 0) { + tabs.push({ id: 'captureScreenshot', label: 'Screenshots' }); + } + + if (otherData.length > 0) { + tabs.push({ id: 'other', label: 'Other' }); + } + + return tabs; + }, [captureListData.length, captureTextData.length, screenshotData.length, otherData.length]); - const { hasScrapeListAction, hasScreenshotAction, hasScrapeSchemaAction } = currentWorkflowActionsState + const availableTabs = getAvailableTabs(); + + useEffect(() => { + if (activeTab >= availableTabs.length && availableTabs.length > 0) { + setActiveTab(0); + } + }, [activeTab, availableTabs.length]); + + const { hasScrapeListAction, hasScreenshotAction, hasScrapeSchemaAction } = currentWorkflowActionsState; useEffect(() => { if (hasScrapeListAction || hasScrapeSchemaAction || hasScreenshotAction) { @@ -127,6 +203,8 @@ export const InterpretationLog: React.FC = ({ isOpen, se const { darkMode } = useThemeMode(); + const getCaptureTextColumns = captureTextData.length > 0 ? Object.keys(captureTextData[0]) : []; + return ( @@ -167,6 +245,7 @@ export const InterpretationLog: React.FC = ({ isOpen, se height: outputPreviewHeight, width: outputPreviewWidth, display: 'flex', + flexDirection: 'column', borderRadius: '10px 10px 0 0', }, }} @@ -175,67 +254,318 @@ export const InterpretationLog: React.FC = ({ isOpen, se {t('interpretation_log.titles.output_preview')} -
- { - binaryData ? ( -
- - {t('interpretation_log.titles.screenshot')} - - {t('interpretation_log.titles.screenshot')} -
- ) : tableData.length > 0 ? ( - <> - - + + {availableTabs.length > 0 ? ( + <> + + {availableTabs.map((tab, index) => ( + setActiveTab(index)} + sx={{ + px: 4, + py: 2, + cursor: 'pointer', + borderBottom: activeTab === index ? '2px solid' : 'none', + borderColor: activeTab === index ? (darkMode ? '#ff00c3' : '#ff00c3') : 'transparent', + backgroundColor: activeTab === index ? (darkMode ? '#34404d' : '#e9ecef') : 'transparent', + color: darkMode ? 'white' : 'black', + fontWeight: activeTab === index ? 500 : 400, + textAlign: 'center', + position: 'relative', + '&:hover': { + backgroundColor: activeTab !== index ? (darkMode ? '#303b49' : '#e2e6ea') : undefined + } + }} + > + + {tab.label} + + + ))} + + + + {activeTab === availableTabs.findIndex(tab => tab.id === 'captureList') && captureListData.length > 0 && ( + + + + {`Table ${captureListPage + 1} of ${captureListData.length}`} + + + + + + + +
+ + + {captureListData[captureListPage] && captureListData[captureListPage].length > 0 && + Object.keys(captureListData[captureListPage][0]).map((column) => ( + + {column} + + )) + } + + + + {captureListData[captureListPage] && + captureListData[captureListPage].map((row: any, idx: any) => ( + + {Object.keys(row).map((column) => ( + + {row[column]} + + ))} + + ))} + +
+
+ + )} + + {activeTab === availableTabs.findIndex(tab => tab.id === 'captureScreenshot') && ( + + {screenshotData.length > 1 && ( + + + {`Screenshot ${screenshotPage + 1} of ${screenshotData.length}`} + + + + + + + )} + {screenshotData.length > 0 && ( + + + {t('interpretation_log.titles.screenshot')} {screenshotPage + 1} + + {`${t('interpretation_log.titles.screenshot')} + + )} + + )} + + {activeTab === availableTabs.findIndex(tab => tab.id === 'captureText') && ( + + - {columns.map((column) => ( - {column} + {getCaptureTextColumns.map((column) => ( + + {column} + ))} - {tableData.slice(0, Math.min(5, tableData.length)).map((row, index) => ( - - {columns.map((column) => ( - {row[column]} + {captureTextData.map((row, idx) => ( + + {getCaptureTextColumns.map((column) => ( + + {row[column]} + ))} ))}
- - {t('interpretation_log.messages.additional_rows')} - - - ) : ( - - - {hasScrapeListAction || hasScrapeSchemaAction || hasScreenshotAction ? ( - <> - - {t('interpretation_log.messages.successful_training')} + )} + + {activeTab === availableTabs.findIndex(tab => tab.id === 'other') && otherData.length > 0 && ( + + {otherData.length > 1 && ( + + + {`Dataset ${otherPage + 1} of ${otherData.length}`} - - - ) : ( - - {t('interpretation_log.messages.no_selection')} - + + + + + )} - - - )} -
-
+ + + + + {otherData[otherPage] && otherData[otherPage].length > 0 && + Object.keys(otherData[otherPage][0]).map((column) => ( + + {column} + + )) + } + + + + {otherData[otherPage] && + otherData[otherPage].map((row: any, idx: any) => ( + + {Object.keys(row).map((column) => ( + + {row[column]} + + ))} + + ))} + +
+
+ + )} + + + ) : ( + + + {hasScrapeListAction || hasScrapeSchemaAction || hasScreenshotAction ? ( + <> + + {t('interpretation_log.messages.successful_training')} + + + + ) : ( + + {t('interpretation_log.messages.no_selection')} + + )} + + + )} +