Merge branch 'develop' into recorder-ui-shift
This commit is contained in:
@@ -13,7 +13,7 @@ import {
|
||||
export const BrowserContent = () => {
|
||||
const { socket } = useSocketStore();
|
||||
|
||||
const [tabs, setTabs] = useState<string[]>(["current"]);
|
||||
const [tabs, setTabs] = useState<string[]>(["Loading..."]);
|
||||
const [tabIndex, setTabIndex] = React.useState(0);
|
||||
const [showOutputData, setShowOutputData] = useState(false);
|
||||
const { browserWidth } = useBrowserDimensionsStore();
|
||||
@@ -125,7 +125,7 @@ export const BrowserContent = () => {
|
||||
useEffect(() => {
|
||||
getCurrentTabs()
|
||||
.then((response) => {
|
||||
if (response) {
|
||||
if (response && response.length > 0) {
|
||||
setTabs(response);
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import React, { useCallback, useContext, useEffect, useRef, useState } from 'react';
|
||||
import { useSocketStore } from '../../context/socket';
|
||||
import { Button } from '@mui/material';
|
||||
import Canvas from "../recorder/Canvas";
|
||||
import { Highlighter } from "../recorder/Highlighter";
|
||||
import { GenericModal } from '../ui/GenericModal';
|
||||
import { useActionContext } from '../../context/browserActions';
|
||||
import { useBrowserSteps, TextStep, ListStep } from '../../context/browserSteps';
|
||||
@@ -38,12 +36,6 @@ interface AttributeOption {
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface ScreencastData {
|
||||
image: string;
|
||||
userId: string;
|
||||
viewport?: ViewportInfo | null;
|
||||
}
|
||||
|
||||
interface ViewportInfo {
|
||||
width: number;
|
||||
height: number;
|
||||
@@ -146,8 +138,6 @@ const getAttributeOptions = (tagName: string, elementInfo: ElementInfo | null):
|
||||
export const BrowserWindow = () => {
|
||||
const { t } = useTranslation();
|
||||
const { browserWidth, browserHeight } = useBrowserDimensionsStore();
|
||||
const [canvasRef, setCanvasReference] = useState<React.RefObject<HTMLCanvasElement> | undefined>(undefined);
|
||||
const [screenShot, setScreenShot] = useState<string>("");
|
||||
const [highlighterData, setHighlighterData] = useState<{
|
||||
rect: DOMRect;
|
||||
selector: string;
|
||||
@@ -1174,16 +1164,31 @@ export const BrowserWindow = () => {
|
||||
undefined,
|
||||
false
|
||||
);
|
||||
|
||||
if (pendingNotification) {
|
||||
notify(pendingNotification.type, pendingNotification.message);
|
||||
setPendingNotification(null);
|
||||
}
|
||||
} else {
|
||||
console.warn(`Failed to extract any fields from list selector: ${listSelector}`);
|
||||
|
||||
setListSelector(null);
|
||||
setFields({});
|
||||
setCachedListSelector(null);
|
||||
setCachedChildSelectors([]);
|
||||
setCurrentListId(null);
|
||||
setInitialAutoFieldIds(new Set());
|
||||
setPendingNotification(null);
|
||||
|
||||
notify(
|
||||
"error",
|
||||
"The list you have selected is not valid. Please reselect it."
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error during child selector caching:", error);
|
||||
} finally {
|
||||
setIsCachingChildSelectors(false);
|
||||
|
||||
if (pendingNotification) {
|
||||
notify(pendingNotification.type, pendingNotification.message);
|
||||
setPendingNotification(null);
|
||||
}
|
||||
}
|
||||
}, 100);
|
||||
} else {
|
||||
@@ -1303,17 +1308,6 @@ export const BrowserWindow = () => {
|
||||
}, []);
|
||||
|
||||
const onMouseMove = (e: MouseEvent) => {
|
||||
if (canvasRef && canvasRef.current && highlighterData) {
|
||||
const canvasRect = canvasRef.current.getBoundingClientRect();
|
||||
if (
|
||||
e.pageX < canvasRect.left
|
||||
|| e.pageX > canvasRect.right
|
||||
|| e.pageY < canvasRect.top
|
||||
|| e.pageY > canvasRect.bottom
|
||||
) {
|
||||
setHighlighterData(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const resetListState = useCallback(() => {
|
||||
@@ -1331,35 +1325,15 @@ export const BrowserWindow = () => {
|
||||
}
|
||||
}, [getList, resetListState]);
|
||||
|
||||
const screencastHandler = useCallback((data: string | ScreencastData) => {
|
||||
if (typeof data === 'string') {
|
||||
setScreenShot(data);
|
||||
} else if (data && typeof data === 'object' && 'image' in data) {
|
||||
if (!data.userId || data.userId === user?.id) {
|
||||
setScreenShot(data.image);
|
||||
|
||||
if (data.viewport) {
|
||||
setViewportInfo(data.viewport);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [user?.id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (socket) {
|
||||
socket.on("screencast", screencastHandler);
|
||||
socket.on("domcast", rrwebSnapshotHandler);
|
||||
socket.on("dom-mode-enabled", domModeHandler);
|
||||
socket.on("dom-mode-error", domModeErrorHandler);
|
||||
}
|
||||
|
||||
if (canvasRef?.current && !isDOMMode && screenShot) {
|
||||
drawImage(screenShot, canvasRef.current);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (socket) {
|
||||
socket.off("screencast", screencastHandler);
|
||||
socket.off("domcast", rrwebSnapshotHandler);
|
||||
socket.off("dom-mode-enabled", domModeHandler);
|
||||
socket.off("dom-mode-error", domModeErrorHandler);
|
||||
@@ -1367,10 +1341,6 @@ export const BrowserWindow = () => {
|
||||
};
|
||||
}, [
|
||||
socket,
|
||||
screenShot,
|
||||
canvasRef,
|
||||
isDOMMode,
|
||||
screencastHandler,
|
||||
rrwebSnapshotHandler,
|
||||
domModeHandler,
|
||||
domModeErrorHandler,
|
||||
@@ -1710,16 +1680,17 @@ export const BrowserWindow = () => {
|
||||
let cleanedSelector = highlighterData.selector;
|
||||
|
||||
setListSelector(cleanedSelector);
|
||||
notify(
|
||||
`info`,
|
||||
t(
|
||||
setPendingNotification({
|
||||
type: `info`,
|
||||
message: t(
|
||||
"browser_window.attribute_modal.notifications.list_select_success",
|
||||
{
|
||||
count: highlighterData.groupInfo.groupSize,
|
||||
}
|
||||
) ||
|
||||
`Selected group with ${highlighterData.groupInfo.groupSize} similar elements`
|
||||
);
|
||||
`Selected group with ${highlighterData.groupInfo.groupSize} similar elements`,
|
||||
count: highlighterData.groupInfo.groupSize,
|
||||
});
|
||||
setCurrentListId(Date.now());
|
||||
setFields({});
|
||||
|
||||
@@ -1847,24 +1818,7 @@ export const BrowserWindow = () => {
|
||||
|
||||
const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (highlighterData) {
|
||||
let shouldProcessClick = false;
|
||||
|
||||
if (!isDOMMode && canvasRef?.current) {
|
||||
const canvasRect = canvasRef.current.getBoundingClientRect();
|
||||
const clickX = e.clientX - canvasRect.left;
|
||||
const clickY = e.clientY - canvasRect.top;
|
||||
const highlightRect = highlighterData.rect;
|
||||
const mappedRect =
|
||||
coordinateMapper.mapBrowserRectToCanvas(highlightRect);
|
||||
|
||||
shouldProcessClick =
|
||||
clickX >= mappedRect.left &&
|
||||
clickX <= mappedRect.right &&
|
||||
clickY >= mappedRect.top &&
|
||||
clickY <= mappedRect.bottom;
|
||||
} else {
|
||||
shouldProcessClick = true;
|
||||
}
|
||||
const shouldProcessClick = true;
|
||||
|
||||
if (shouldProcessClick) {
|
||||
const options = getAttributeOptions(
|
||||
@@ -2209,17 +2163,7 @@ export const BrowserWindow = () => {
|
||||
!showAttributeModal &&
|
||||
highlighterData?.rect != null && (
|
||||
<>
|
||||
{!isDOMMode && canvasRef?.current && (
|
||||
<Highlighter
|
||||
unmodifiedRect={highlighterData?.rect}
|
||||
displayedSelector={highlighterData?.selector}
|
||||
width={dimensions.width}
|
||||
height={dimensions.height}
|
||||
canvasRect={canvasRef.current.getBoundingClientRect()}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isDOMMode && highlighterData && (
|
||||
{highlighterData && (
|
||||
<div
|
||||
id="dom-highlight-overlay"
|
||||
style={{
|
||||
@@ -2355,31 +2299,27 @@ export const BrowserWindow = () => {
|
||||
borderRadius: "0px 0px 5px 5px",
|
||||
}}
|
||||
>
|
||||
{isDOMMode ? (
|
||||
{currentSnapshot ? (
|
||||
<>
|
||||
{currentSnapshot ? (
|
||||
<DOMBrowserRenderer
|
||||
width={dimensions.width}
|
||||
height={dimensions.height}
|
||||
snapshot={currentSnapshot}
|
||||
getList={getList}
|
||||
getText={getText}
|
||||
listSelector={listSelector}
|
||||
cachedChildSelectors={cachedChildSelectors}
|
||||
paginationMode={paginationMode}
|
||||
paginationType={paginationType}
|
||||
limitMode={limitMode}
|
||||
isCachingChildSelectors={isCachingChildSelectors}
|
||||
onHighlight={domHighlighterHandler}
|
||||
onElementSelect={handleDOMElementSelection}
|
||||
onShowDatePicker={handleShowDatePicker}
|
||||
onShowDropdown={handleShowDropdown}
|
||||
onShowTimePicker={handleShowTimePicker}
|
||||
onShowDateTimePicker={handleShowDateTimePicker}
|
||||
/>
|
||||
) : (
|
||||
<DOMLoadingIndicator />
|
||||
)}
|
||||
<DOMBrowserRenderer
|
||||
width={dimensions.width}
|
||||
height={dimensions.height}
|
||||
snapshot={currentSnapshot}
|
||||
getList={getList}
|
||||
getText={getText}
|
||||
listSelector={listSelector}
|
||||
cachedChildSelectors={cachedChildSelectors}
|
||||
paginationMode={paginationMode}
|
||||
paginationType={paginationType}
|
||||
limitMode={limitMode}
|
||||
isCachingChildSelectors={isCachingChildSelectors}
|
||||
onHighlight={domHighlighterHandler}
|
||||
onElementSelect={handleDOMElementSelection}
|
||||
onShowDatePicker={handleShowDatePicker}
|
||||
onShowDropdown={handleShowDropdown}
|
||||
onShowTimePicker={handleShowTimePicker}
|
||||
onShowDateTimePicker={handleShowDateTimePicker}
|
||||
/>
|
||||
|
||||
{/* --- Loading overlay --- */}
|
||||
{isCachingChildSelectors && (
|
||||
@@ -2492,11 +2432,7 @@ export const BrowserWindow = () => {
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<Canvas
|
||||
onCreateRef={setCanvasReference}
|
||||
width={dimensions.width}
|
||||
height={dimensions.height}
|
||||
/>
|
||||
<DOMLoadingIndicator />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -2591,26 +2527,6 @@ const DOMLoadingIndicator: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const drawImage = (image: string, canvas: HTMLCanvasElement): void => {
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) return;
|
||||
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
requestAnimationFrame(() => {
|
||||
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
||||
});
|
||||
if (image.startsWith('blob:')) {
|
||||
URL.revokeObjectURL(image);
|
||||
}
|
||||
};
|
||||
img.onerror = () => {
|
||||
console.warn('Failed to load image');
|
||||
};
|
||||
img.src = image;
|
||||
};
|
||||
|
||||
const modalStyle = {
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
|
||||
@@ -119,7 +119,7 @@ export const NavBar: React.FC<NavBarProps> = ({
|
||||
} catch (error: any) {
|
||||
const status = error.response?.status;
|
||||
let errorKey = 'unknown';
|
||||
|
||||
|
||||
switch (status) {
|
||||
case 401:
|
||||
errorKey = 'unauthorized';
|
||||
@@ -132,7 +132,7 @@ export const NavBar: React.FC<NavBarProps> = ({
|
||||
errorKey = 'network';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
notify(
|
||||
'error',
|
||||
t(`navbar.notifications.errors.logout.${errorKey}`, {
|
||||
@@ -163,6 +163,9 @@ export const NavBar: React.FC<NavBarProps> = ({
|
||||
onClick={toggleTheme}
|
||||
sx={{
|
||||
color: darkMode ? '#ffffff' : '#0000008A',
|
||||
'&:hover': {
|
||||
background: 'inherit'
|
||||
}
|
||||
}}
|
||||
>
|
||||
{darkMode ? <LightMode /> : <DarkMode />}
|
||||
@@ -253,6 +256,9 @@ export const NavBar: React.FC<NavBarProps> = ({
|
||||
borderRadius: '5px',
|
||||
padding: '8px',
|
||||
marginRight: '20px',
|
||||
'&:hover': {
|
||||
background: 'inherit'
|
||||
}
|
||||
}}>
|
||||
<Update sx={{ marginRight: '5px' }} />
|
||||
<Typography variant="body1">{t('navbar.upgrade.button')}</Typography>
|
||||
@@ -332,7 +338,7 @@ export const NavBar: React.FC<NavBarProps> = ({
|
||||
docker-compose down
|
||||
<br />
|
||||
<br />
|
||||
# Remove existing backend and frontend images
|
||||
# Remove existing backend and frontend images
|
||||
<br />
|
||||
docker rmi getmaxun/maxun-frontend:latest getmaxun/maxun-backend:latest
|
||||
<br />
|
||||
@@ -367,7 +373,7 @@ export const NavBar: React.FC<NavBarProps> = ({
|
||||
padding: '8px',
|
||||
marginRight: '10px',
|
||||
'&:hover': {
|
||||
background: 'inherit'
|
||||
background: 'inherit'
|
||||
}
|
||||
}}>
|
||||
<AccountCircle sx={{ marginRight: '5px' }} />
|
||||
|
||||
@@ -552,16 +552,12 @@ export const RobotEditPage = ({ handleStart }: RobotSettingsProps) => {
|
||||
pair.what.forEach((action, actionIndex) => {
|
||||
if (!editableActions.has(String(action.action))) return;
|
||||
|
||||
let currentName =
|
||||
action.name ||
|
||||
(action.args && action.args[0] && typeof action.args[0] === 'object') ||
|
||||
'';
|
||||
let currentName = action.name || '';
|
||||
|
||||
if (!currentName) {
|
||||
switch (action.action) {
|
||||
case 'scrapeSchema':
|
||||
textCount++;
|
||||
currentName = `Text ${textCount}`;
|
||||
currentName = 'Texts';
|
||||
break;
|
||||
case 'screenshot':
|
||||
screenshotCount++;
|
||||
@@ -574,9 +570,6 @@ export const RobotEditPage = ({ handleStart }: RobotSettingsProps) => {
|
||||
}
|
||||
} else {
|
||||
switch (action.action) {
|
||||
case 'scrapeSchema':
|
||||
textCount++;
|
||||
break;
|
||||
case 'screenshot':
|
||||
screenshotCount++;
|
||||
break;
|
||||
@@ -599,10 +592,7 @@ export const RobotEditPage = ({ handleStart }: RobotSettingsProps) => {
|
||||
|
||||
switch (action.action) {
|
||||
case 'scrapeSchema': {
|
||||
const existingName =
|
||||
currentName ||
|
||||
(action.args && action.args[0] && typeof action.args[0] === "object") ||
|
||||
"Texts";
|
||||
const existingName = currentName || "Texts";
|
||||
|
||||
if (!textInputs.length) {
|
||||
textInputs.push(
|
||||
|
||||
@@ -61,7 +61,7 @@ export const InterpretationLog: React.FC<InterpretationLogProps> = ({ isOpen, se
|
||||
const { captureStage, getText } = useActionContext();
|
||||
|
||||
const { browserWidth, outputPreviewHeight, outputPreviewWidth } = useBrowserDimensionsStore();
|
||||
const { currentWorkflowActionsState, shouldResetInterpretationLog, currentTextGroupName, setCurrentTextGroupName } = useGlobalInfoStore();
|
||||
const { currentWorkflowActionsState, shouldResetInterpretationLog, currentTextGroupName, setCurrentTextGroupName, notify } = useGlobalInfoStore();
|
||||
|
||||
const [showPreviewData, setShowPreviewData] = useState<boolean>(false);
|
||||
const userClosedDrawer = useRef<boolean>(false);
|
||||
@@ -154,6 +154,28 @@ export const InterpretationLog: React.FC<InterpretationLogProps> = ({ isOpen, se
|
||||
}
|
||||
};
|
||||
|
||||
const checkForDuplicateName = (stepId: number, type: 'list' | 'text' | 'screenshot', newName: string): boolean => {
|
||||
const trimmedName = newName.trim();
|
||||
|
||||
if (type === 'list') {
|
||||
const listSteps = browserSteps.filter(step => step.type === 'list' && step.id !== stepId);
|
||||
const duplicate = listSteps.find(step => step.name === trimmedName);
|
||||
if (duplicate) {
|
||||
notify('error', `A list with the name "${trimmedName}" already exists. Please choose a different name.`);
|
||||
return true;
|
||||
}
|
||||
} else if (type === 'screenshot') {
|
||||
const screenshotSteps = browserSteps.filter(step => step.type === 'screenshot' && step.id !== stepId);
|
||||
const duplicate = screenshotSteps.find(step => step.name === trimmedName);
|
||||
if (duplicate) {
|
||||
notify('error', `A screenshot with the name "${trimmedName}" already exists. Please choose a different name.`);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const startEdit = (stepId: number, type: 'list' | 'text' | 'screenshot', currentValue: string) => {
|
||||
setEditing({ stepId, type, value: currentValue });
|
||||
};
|
||||
@@ -168,6 +190,10 @@ export const InterpretationLog: React.FC<InterpretationLogProps> = ({ isOpen, se
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkForDuplicateName(stepId, type, finalValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (type === 'list') {
|
||||
updateListStepName(stepId, finalValue);
|
||||
} else if (type === 'text') {
|
||||
@@ -306,6 +332,8 @@ export const InterpretationLog: React.FC<InterpretationLogProps> = ({ isOpen, se
|
||||
shouldOpenDrawer = true;
|
||||
}
|
||||
lastListDataLength.current = captureListData.length;
|
||||
} else if (hasScrapeListAction && captureListData.length === 0) {
|
||||
lastListDataLength.current = 0;
|
||||
}
|
||||
|
||||
if (hasScrapeSchemaAction && captureTextData.length > 0 && !getText) {
|
||||
@@ -315,6 +343,8 @@ export const InterpretationLog: React.FC<InterpretationLogProps> = ({ isOpen, se
|
||||
shouldOpenDrawer = true;
|
||||
}
|
||||
lastTextDataLength.current = captureTextData.length;
|
||||
} else if (hasScrapeSchemaAction && captureTextData.length === 0) {
|
||||
lastTextDataLength.current = 0;
|
||||
}
|
||||
|
||||
if (hasScreenshotAction && screenshotData.length > 0) {
|
||||
@@ -324,6 +354,8 @@ export const InterpretationLog: React.FC<InterpretationLogProps> = ({ isOpen, se
|
||||
shouldOpenDrawer = true;
|
||||
}
|
||||
lastScreenshotDataLength.current = screenshotData.length;
|
||||
} else if (hasScreenshotAction && screenshotData.length === 0) {
|
||||
lastScreenshotDataLength.current = 0;
|
||||
}
|
||||
|
||||
const getLatestCaptureType = () => {
|
||||
@@ -466,7 +498,7 @@ export const InterpretationLog: React.FC<InterpretationLogProps> = ({ isOpen, se
|
||||
{t('interpretation_log.titles.output_preview')}
|
||||
</Typography>
|
||||
|
||||
{!(hasScrapeListAction || hasScrapeSchemaAction || hasScreenshotAction) && (
|
||||
{!(hasScrapeListAction || hasScrapeSchemaAction || hasScreenshotAction) && !showPreviewData && availableTabs.length === 0 && (
|
||||
<Grid container justifyContent="center" alignItems="center" style={{ height: '100%' }}>
|
||||
<Grid item>
|
||||
<Typography variant="h6" gutterBottom align="left">
|
||||
|
||||
@@ -115,27 +115,29 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe
|
||||
const rawKeys = Object.keys(row.binaryOutput);
|
||||
|
||||
const isLegacyPattern = rawKeys.every(key => /^item-\d+-\d+$/.test(key));
|
||||
|
||||
let normalizedScreenshotKeys: string[];
|
||||
|
||||
if (isLegacyPattern) {
|
||||
const renamedKeys = rawKeys.map((_, index) => `Screenshot ${index + 1}`);
|
||||
const keyMap: Record<string, string> = {};
|
||||
|
||||
renamedKeys.forEach((displayName, index) => {
|
||||
keyMap[displayName] = rawKeys[index];
|
||||
});
|
||||
|
||||
setScreenshotKeys(renamedKeys);
|
||||
setScreenshotKeyMap(keyMap);
|
||||
// Legacy unnamed screenshots → Screenshot 1, Screenshot 2...
|
||||
normalizedScreenshotKeys = rawKeys.map((_, index) => `Screenshot ${index + 1}`);
|
||||
} else {
|
||||
const keyMap: Record<string, string> = {};
|
||||
rawKeys.forEach(key => {
|
||||
keyMap[key] = key;
|
||||
// Same rule as captured lists: if name missing or generic, auto-label
|
||||
normalizedScreenshotKeys = rawKeys.map((key, index) => {
|
||||
if (!key || key.toLowerCase().includes("screenshot")) {
|
||||
return `Screenshot ${index + 1}`;
|
||||
}
|
||||
return key;
|
||||
});
|
||||
|
||||
setScreenshotKeys(rawKeys);
|
||||
setScreenshotKeyMap(keyMap);
|
||||
}
|
||||
|
||||
const keyMap: Record<string, string> = {};
|
||||
normalizedScreenshotKeys.forEach((displayName, index) => {
|
||||
keyMap[displayName] = rawKeys[index];
|
||||
});
|
||||
|
||||
setScreenshotKeys(normalizedScreenshotKeys);
|
||||
setScreenshotKeyMap(keyMap);
|
||||
setCurrentScreenshotIndex(0);
|
||||
} else {
|
||||
setScreenshotKeys([]);
|
||||
@@ -202,7 +204,14 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe
|
||||
|
||||
const processSchemaData = (schemaOutput: any) => {
|
||||
const keys = Object.keys(schemaOutput);
|
||||
setSchemaKeys(keys);
|
||||
const normalizedKeys = keys.map((key, index) => {
|
||||
if (!key || key.toLowerCase().includes("scrapeschema")) {
|
||||
return keys.length === 1 ? "Texts" : `Text ${index + 1}`;
|
||||
}
|
||||
return key;
|
||||
});
|
||||
|
||||
setSchemaKeys(normalizedKeys);
|
||||
|
||||
const dataByKey: Record<string, any[]> = {};
|
||||
const columnsByKey: Record<string, string[]> = {};
|
||||
@@ -248,8 +257,17 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe
|
||||
}
|
||||
});
|
||||
|
||||
setSchemaDataByKey(dataByKey);
|
||||
setSchemaColumnsByKey(columnsByKey);
|
||||
const remappedDataByKey: Record<string, any[]> = {};
|
||||
const remappedColumnsByKey: Record<string, string[]> = {};
|
||||
|
||||
normalizedKeys.forEach((newKey, idx) => {
|
||||
const oldKey = keys[idx];
|
||||
remappedDataByKey[newKey] = dataByKey[oldKey];
|
||||
remappedColumnsByKey[newKey] = columnsByKey[oldKey];
|
||||
});
|
||||
|
||||
setSchemaDataByKey(remappedDataByKey);
|
||||
setSchemaColumnsByKey(remappedColumnsByKey);
|
||||
|
||||
if (allData.length > 0) {
|
||||
const allColumns = new Set<string>();
|
||||
@@ -290,7 +308,14 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe
|
||||
|
||||
setListData(tablesList);
|
||||
setListColumns(columnsList);
|
||||
setListKeys(keys);
|
||||
const normalizedListKeys = keys.map((key, index) => {
|
||||
if (!key || key.toLowerCase().includes("scrapelist")) {
|
||||
return `List ${index + 1}`;
|
||||
}
|
||||
return key;
|
||||
});
|
||||
|
||||
setListKeys(normalizedListKeys);
|
||||
setCurrentListIndex(0);
|
||||
};
|
||||
|
||||
@@ -617,10 +642,15 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe
|
||||
<TabContext value={tab}>
|
||||
<TabPanel value='output' sx={{ width: '100%', maxWidth: '900px' }}>
|
||||
{row.status === 'running' || row.status === 'queued' ? (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<CircularProgress size={22} sx={{ marginRight: '10px' }} />
|
||||
{t('run_content.loading')}
|
||||
</Box>
|
||||
<>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
|
||||
<CircularProgress size={22} sx={{ marginRight: '10px' }} />
|
||||
{t('run_content.loading')}
|
||||
</Box>
|
||||
<Button color="error" onClick={abortRunHandler} sx={{ mt: 1 }}>
|
||||
{t('run_content.buttons.stop')}
|
||||
</Button>
|
||||
</>
|
||||
) : (!hasData && !hasScreenshots
|
||||
? <Typography>{t('run_content.empty_output')}</Typography>
|
||||
: null)}
|
||||
|
||||
@@ -133,6 +133,10 @@ export const PageWrapper = () => {
|
||||
path="/register"
|
||||
element={<Register />}
|
||||
/>
|
||||
<Route
|
||||
path="/recording-setup"
|
||||
element={<div />}
|
||||
/>
|
||||
<Route path="*" element={<NotFoundPage />} />
|
||||
</Routes>
|
||||
</Box>
|
||||
|
||||
@@ -43,7 +43,7 @@ export const RecordingPage = ({ recordingName }: RecordingPageProps) => {
|
||||
|
||||
const { setId, socket } = useSocketStore();
|
||||
const { setWidth } = useBrowserDimensionsStore();
|
||||
const { browserId, setBrowserId, recordingId, recordingUrl, setRecordingUrl, setRecordingName, setRetrainRobotId } = useGlobalInfoStore();
|
||||
const { browserId, setBrowserId, recordingId, recordingUrl, setRecordingUrl, setRecordingName, setRetrainRobotId, setIsDOMMode } = useGlobalInfoStore();
|
||||
|
||||
const handleShowOutputData = useCallback(() => {
|
||||
setShowOutputData(true);
|
||||
@@ -77,6 +77,8 @@ export const RecordingPage = ({ recordingName }: RecordingPageProps) => {
|
||||
useEffect(() => {
|
||||
let isCancelled = false;
|
||||
const handleRecording = async () => {
|
||||
setIsDOMMode(true);
|
||||
|
||||
const storedUrl = window.sessionStorage.getItem('recordingUrl');
|
||||
if (storedUrl && !recordingUrl) {
|
||||
setRecordingUrl(storedUrl);
|
||||
@@ -137,9 +139,12 @@ export const RecordingPage = ({ recordingName }: RecordingPageProps) => {
|
||||
if (browserId === 'new-recording') {
|
||||
socket?.emit('new-recording');
|
||||
}
|
||||
if (recordingUrl && socket) {
|
||||
socket.emit('input:url', recordingUrl);
|
||||
}
|
||||
setIsLoaded(true);
|
||||
}
|
||||
}, [socket, browserId, recordingName, recordingId, isLoaded]);
|
||||
}, [socket, browserId, recordingName, recordingId, recordingUrl, isLoaded]);
|
||||
|
||||
useEffect(() => {
|
||||
socket?.on('loaded', handleLoaded);
|
||||
@@ -153,26 +158,20 @@ export const RecordingPage = ({ recordingName }: RecordingPageProps) => {
|
||||
<ActionProvider>
|
||||
<BrowserStepsProvider>
|
||||
<div id="browser-recorder">
|
||||
{isLoaded ? (
|
||||
<>
|
||||
<Grid container direction="row" style={{ flexGrow: 1, height: '100%' }}>
|
||||
<Grid item xs={12} md={9} lg={9} style={{ height: '100%', overflow: 'hidden', position: 'relative' }}>
|
||||
<div style={{ height: '100%', overflow: 'hidden', display: 'flex', flexDirection: 'column', }}>
|
||||
<BrowserContent />
|
||||
<InterpretationLog isOpen={showOutputData} setIsOpen={setShowOutputData} />
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={3} lg={3} style={{ height: '100%', overflow: 'hidden' }}>
|
||||
<div className="right-side-panel" style={{ height: '100%' }}>
|
||||
<RightSidePanel onFinishCapture={handleShowOutputData} />
|
||||
<BrowserRecordingSave />
|
||||
</div>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</>
|
||||
) : (
|
||||
<Loader text={t('recording_page.loader.browser_startup', { url: recordingUrl })} />
|
||||
)}
|
||||
<Grid container direction="row" style={{ flexGrow: 1, height: '100%' }}>
|
||||
<Grid item xs={12} md={9} lg={9} style={{ height: '100%', overflow: 'hidden', position: 'relative' }}>
|
||||
<div style={{ height: '100%', overflow: 'auto' }}>
|
||||
<BrowserContent />
|
||||
<InterpretationLog isOpen={showOutputData} setIsOpen={setShowOutputData} />
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={3} lg={3} style={{ height: '100%', overflow: 'hidden' }}>
|
||||
<div className="right-side-panel" style={{ height: '100%' }}>
|
||||
<RightSidePanel onFinishCapture={handleShowOutputData} />
|
||||
<BrowserRecordingSave />
|
||||
</div>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</div>
|
||||
</BrowserStepsProvider>
|
||||
</ActionProvider>
|
||||
|
||||
Reference in New Issue
Block a user