diff --git a/public/locales/en.json b/public/locales/en.json
index c9f218d8..da0ebcc7 100644
--- a/public/locales/en.json
+++ b/public/locales/en.json
@@ -580,7 +580,7 @@
"buttons": {
"stop": "Stop"
},
- "loading": "Loading data...",
+ "loading": "Extracting data...",
"empty_output": "No output data available",
"captured_data": {
"title": "Captured Data",
diff --git a/src/api/storage.ts b/src/api/storage.ts
index e584c36f..b5dc32de 100644
--- a/src/api/storage.ts
+++ b/src/api/storage.ts
@@ -1,7 +1,7 @@
import { default as axios } from "axios";
import { WorkflowFile } from "maxun-core";
import { RunSettings } from "../components/run/RunSettings";
-import { ScheduleSettings } from "../components/robot/ScheduleSettings";
+import { ScheduleSettings } from "../components/robot/pages/ScheduleSettingsPage";
import { CreateRunResponse, ScheduleRunResponse } from "../pages/MainPage";
import { apiUrl } from "../apiConfig";
diff --git a/src/components/api/ApiKey.tsx b/src/components/api/ApiKey.tsx
index 9feb9551..27897169 100644
--- a/src/components/api/ApiKey.tsx
+++ b/src/components/api/ApiKey.tsx
@@ -26,7 +26,8 @@ const Container = styled(Box)`
flex-direction: column;
align-items: center;
margin-top: 50px;
- margin-left: 50px;
+ margin-left: 70px;
+ margin-right: 70px;
`;
const ApiKeyManager = () => {
@@ -108,7 +109,7 @@ const ApiKeyManager = () => {
return (
-
+
Start by creating an API key below. Then,
test your API
@@ -139,7 +140,7 @@ const ApiKeyManager = () => {
{apiKeyName}
-
+
{showKey ? `${apiKey?.substring(0, 10)}...` : '**********'}
@@ -174,6 +175,5 @@ const ApiKeyManager = () => {
)}
);
-};
-
+}
export default ApiKeyManager;
\ No newline at end of file
diff --git a/src/components/dashboard/MainMenu.tsx b/src/components/dashboard/MainMenu.tsx
index 8b500d62..cd98ef21 100644
--- a/src/components/dashboard/MainMenu.tsx
+++ b/src/components/dashboard/MainMenu.tsx
@@ -72,7 +72,7 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp
{
}, []);
return (
- <>
-
-
+
+
+
{t('proxy.title')}
-
+
{tabIndex === 0 && (
isProxyConfigured ? (
-
-
+
+
@@ -187,13 +198,13 @@ const ProxyForm: React.FC = () => {
-
)}
@@ -197,4 +190,4 @@ export const RobotConfigPage: React.FC = ({
)}
);
-};
+}
diff --git a/src/components/robot/pages/ScheduleSettingsPage.tsx b/src/components/robot/pages/ScheduleSettingsPage.tsx
index e386cfec..8933044b 100644
--- a/src/components/robot/pages/ScheduleSettingsPage.tsx
+++ b/src/components/robot/pages/ScheduleSettingsPage.tsx
@@ -7,7 +7,6 @@ import {
Box,
Button,
} from "@mui/material";
-import { Schedule } from "@mui/icons-material";
import { Dropdown } from "../../ui/DropdownMui";
import { validMomentTimezones } from "../../../constants/const";
import { useGlobalInfoStore } from "../../../context/globalInfo";
@@ -188,8 +187,8 @@ export const ScheduleSettingsPage = ({
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
- "& > *": { marginBottom: "20px" },
- marginTop: "-20px",
+ gap: 3,
+ width: "100%",
}}
>
<>
@@ -215,7 +214,7 @@ export const ScheduleSettingsPage = ({
{t("schedule_settings.at_around")}: {schedule.atTimeStart},{" "}
{schedule.timezone} {t("schedule_settings.timezone")}
-
+
-
+
{t("schedule_settings.labels.run_once_every")}
-
+
{["MONTHS", "WEEKS"].includes(settings.runEveryUnit)
? t("schedule_settings.labels.start_from_label")
: t("schedule_settings.labels.start_from_label")}
@@ -288,7 +287,7 @@ export const ScheduleSettingsPage = ({
{settings.runEveryUnit === "MONTHS" && (
-
+
{t("schedule_settings.labels.on_day_of_month")}
-
-
- {t("schedule_settings.labels.in_between")}
-
+
+ {t("schedule_settings.labels.in_between")}
+
+
) : (
-
+
{t("schedule_settings.at_around")}
-
+
{t("schedule_settings.timezone")}
);
-};
+}
diff --git a/src/components/run/InterpretationLog.tsx b/src/components/run/InterpretationLog.tsx
index 74c46f4e..a0042694 100644
--- a/src/components/run/InterpretationLog.tsx
+++ b/src/components/run/InterpretationLog.tsx
@@ -41,17 +41,14 @@ export const InterpretationLog: React.FC = ({ isOpen, se
const [editingField, setEditingField] = useState<{listId: number, fieldKey: string} | null>(null);
const [editingValue, setEditingValue] = useState('');
- const [editingListName, setEditingListName] = useState(null);
- const [editingListNameValue, setEditingListNameValue] = useState('');
-
const [editingTextGroupName, setEditingTextGroupName] = useState(false);
const [editingTextGroupNameValue, setEditingTextGroupNameValue] = useState('Text Data');
- const [editingTextLabel, setEditingTextLabel] = useState(null);
- const [editingTextLabelValue, setEditingTextLabelValue] = useState('');
-
- const [editingScreenshotName, setEditingScreenshotName] = useState(null);
- const [editingScreenshotNameValue, setEditingScreenshotNameValue] = useState('');
+ const [editing, setEditing] = useState<{
+ stepId: number | null;
+ type: 'list' | 'text' | 'screenshot' | null;
+ value: string;
+ }>({ stepId: null, type: null, value: '' });
const logEndRef = useRef(null);
const autoFocusedListIds = useRef>(new Set());
@@ -125,30 +122,6 @@ export const InterpretationLog: React.FC = ({ isOpen, se
}
};
- const handleStartEditListName = (listId: number, currentName: string) => {
- setEditingListName(listId);
- setEditingListNameValue(currentName);
- };
-
- const handleSaveListName = () => {
- if (editingListName !== null) {
- const trimmedName = editingListNameValue.trim();
- const finalName = trimmedName || `List Data ${captureListData.findIndex(l => l.id === editingListName) + 1}`;
-
- updateListStepName(editingListName, finalName);
-
- // Use ref-synced version of browserSteps via emitForStepId
- const listStep = browserSteps.find(step => step.id === editingListName);
- if (listStep?.actionId) {
- // small async delay ensures React state commit
- setTimeout(() => emitForStepId(listStep.actionId!), 0);
- }
-
- setEditingListName(null);
- setEditingListNameValue('');
- }
- };
-
const handleStartEditTextGroupName = () => {
setEditingTextGroupName(true);
setEditingTextGroupNameValue(currentTextGroupName);
@@ -158,7 +131,6 @@ export const InterpretationLog: React.FC = ({ isOpen, se
const trimmedName = editingTextGroupNameValue.trim();
const finalName = trimmedName || 'Text Data';
- console.log("SAVING TEXT GROUP NAME:", finalName);
setCurrentTextGroupName(finalName);
setEditingTextGroupName(false);
@@ -169,34 +141,6 @@ export const InterpretationLog: React.FC = ({ isOpen, se
}, 0);
};
-
- const handleStartEditTextLabel = (textId: number, currentLabel: string) => {
- setEditingTextLabel(textId);
- setEditingTextLabelValue(currentLabel);
- };
-
- const handleSaveTextLabel = () => {
- if (editingTextLabel !== null && editingTextLabelValue.trim()) {
- const textStep = browserSteps.find(step => step.id === editingTextLabel);
- const actionId = textStep?.actionId;
-
- updateBrowserTextStepLabel(editingTextLabel, editingTextLabelValue.trim());
-
- // Emit updated action to backend after state update completes
- if (actionId) {
- setTimeout(() => emitForStepId(actionId), 0);
- }
-
- setEditingTextLabel(null);
- setEditingTextLabelValue('');
- }
- };
-
- const handleCancelTextLabel = () => {
- setEditingTextLabel(null);
- setEditingTextLabelValue('');
- };
-
const handleDeleteTextStep = (textId: number) => {
const textStep = browserSteps.find(step => step.id === textId);
const actionId = textStep?.actionId;
@@ -210,36 +154,36 @@ export const InterpretationLog: React.FC = ({ isOpen, se
}
};
- const handleStartEditScreenshotName = (screenshotStepId: number, currentName: string) => {
- setEditingScreenshotName(screenshotStepId);
- setEditingScreenshotNameValue(currentName);
+ const startEdit = (stepId: number, type: 'list' | 'text' | 'screenshot', currentValue: string) => {
+ setEditing({ stepId, type, value: currentValue });
};
- const handleSaveScreenshotName = () => {
- if (editingScreenshotName !== null) {
- const trimmedName = editingScreenshotNameValue.trim();
- const screenshotSteps = browserSteps.filter(step => step.type === 'screenshot');
- const screenshotIndex = screenshotSteps.findIndex(s => s.id === editingScreenshotName);
- const finalName = trimmedName || `Screenshot ${screenshotIndex + 1}`;
-
- updateScreenshotStepName(editingScreenshotName, finalName);
+ const saveEdit = () => {
+ const { stepId, type, value } = editing;
+ if (stepId == null || !type) return;
- const screenshotStep = browserSteps.find(step => step.id === editingScreenshotName);
- if (screenshotStep?.actionId) {
- const originalName = screenshotStep.name?.trim() || "";
- const trimmedName = editingScreenshotNameValue.trim();
-
- // 🚫 Only emit if name actually changed
- if (trimmedName && trimmedName !== originalName) {
- setTimeout(() => emitForStepId(screenshotStep.actionId!), 500);
- } else {
- console.log("🧠Skipping emit — screenshot name unchanged.");
- }
- }
-
- setEditingScreenshotName(null);
- setEditingScreenshotNameValue('');
+ const finalValue = value.trim();
+ if (!finalValue) {
+ setEditing({ stepId: null, type: null, value: '' });
+ return;
}
+
+ if (type === 'list') {
+ updateListStepName(stepId, finalValue);
+ } else if (type === 'text') {
+ updateBrowserTextStepLabel(stepId, finalValue);
+ } else if (type === 'screenshot') {
+ updateScreenshotStepName(stepId, finalValue);
+ }
+
+ const step = browserSteps.find(s => s.id === stepId);
+ if (step?.actionId) setTimeout(() => emitForStepId(step.actionId!), 0);
+
+ setEditing({ stepId: null, type: null, value: '' });
+ };
+
+ const cancelEdit = () => {
+ setEditing({ stepId: null, type: null, value: '' });
};
@@ -354,8 +298,6 @@ export const InterpretationLog: React.FC = ({ isOpen, se
useEffect(() => {
let shouldOpenDrawer = false;
- let switchToTextTab = false;
- let switchToScreenshotTab = false;
if (hasScrapeListAction && captureListData.length > 0 && captureListData[0]?.data?.length > 0) {
setShowPreviewData(true);
@@ -371,7 +313,6 @@ export const InterpretationLog: React.FC = ({ isOpen, se
if (captureTextData.length > lastTextDataLength.current) {
userClosedDrawer.current = false;
shouldOpenDrawer = true;
- switchToTextTab = true;
}
lastTextDataLength.current = captureTextData.length;
}
@@ -381,23 +322,35 @@ export const InterpretationLog: React.FC = ({ isOpen, se
if (screenshotData.length > lastScreenshotDataLength.current) {
userClosedDrawer.current = false;
shouldOpenDrawer = true;
- switchToScreenshotTab = true;
}
lastScreenshotDataLength.current = screenshotData.length;
}
+ const getLatestCaptureType = () => {
+ for (let i = browserSteps.length - 1; i >= 0; i--) {
+ const type = browserSteps[i].type;
+ if (type === "list" || type === "text" || type === "screenshot") {
+ return type;
+ }
+ }
+ return null;
+ };
+
if (shouldOpenDrawer) {
setIsOpen(true);
- if (switchToTextTab) {
- setTimeout(() => {
- const textTabIndex = getAvailableTabs().findIndex(tab => tab.id === 'captureText');
- if (textTabIndex !== -1) {
- setActiveTab(textTabIndex);
- }
- }, 100);
- } else if (switchToScreenshotTab) {
- setTimeout(() => {
- const screenshotTabIndex = getAvailableTabs().findIndex(tab => tab.id === 'captureScreenshot');
+ const latestType = getLatestCaptureType();
+
+ setTimeout(() => {
+ if (latestType === "text") {
+ const idx = getAvailableTabs().findIndex(t => t.id === "captureText");
+ if (idx !== -1) setActiveTab(idx);
+
+ } else if (latestType === "list") {
+ const idx = getAvailableTabs().findIndex(t => t.id === "captureList");
+ if (idx !== -1) setActiveTab(idx);
+
+ } else if (latestType === "screenshot") {
+ const screenshotTabIndex = getAvailableTabs().findIndex(tab => tab.id === "captureScreenshot");
if (screenshotTabIndex !== -1) {
setActiveTab(screenshotTabIndex);
const latestIndex = screenshotData.length - 1;
@@ -406,17 +359,17 @@ export const InterpretationLog: React.FC = ({ isOpen, se
if (!autoFocusedScreenshotIndices.current.has(latestIndex)) {
autoFocusedScreenshotIndices.current.add(latestIndex);
setTimeout(() => {
- const screenshotSteps = browserSteps.filter(step => step.type === 'screenshot') as Array<{ id: number; name?: string; type: 'screenshot' }>;
+ const screenshotSteps = browserSteps.filter(step => step.type === "screenshot");
const latestScreenshotStep = screenshotSteps[latestIndex];
if (latestScreenshotStep) {
const screenshotName = latestScreenshotStep.name || `Screenshot ${latestIndex + 1}`;
- handleStartEditScreenshotName(latestScreenshotStep.id, screenshotName);
+ startEdit(latestScreenshotStep.id, 'screenshot', screenshotName);
}
}, 300);
}
}
- }, 100);
- }
+ }
+ }, 100);
}
}, [hasScrapeListAction, hasScrapeSchemaAction, hasScreenshotAction, captureListData, captureTextData, screenshotData, setIsOpen, getText]);
@@ -424,7 +377,7 @@ export const InterpretationLog: React.FC = ({ isOpen, se
if (captureListData.length > 0 && isOpen && captureStage === 'initial') {
const latestListIndex = captureListData.length - 1;
const latestList = captureListData[latestListIndex];
- if (latestList && latestList.data && latestList.data.length > 0 && !editingListName) {
+ if (latestList && latestList.data && latestList.data.length > 0 && editing.type !== 'list') {
const previousLength = previousDataLengths.current.get(latestList.id) || 0;
const currentLength = latestList.data.length;
@@ -433,7 +386,7 @@ export const InterpretationLog: React.FC = ({ isOpen, se
autoFocusedListIds.current.add(latestList.id);
setActiveListTab(latestListIndex);
setTimeout(() => {
- handleStartEditListName(latestList.id, latestList.name || `List Data ${latestListIndex + 1}`);
+ startEdit(latestList.id, 'list', latestList.name || `List Data ${latestListIndex + 1}`);
}, 300);
}
}
@@ -579,7 +532,7 @@ export const InterpretationLog: React.FC = ({ isOpen, se
}}
>
{captureListData.map((listItem, index) => {
- const isEditing = editingListName === listItem.id;
+ const isEditing = editing.stepId === listItem.id && editing.type === 'list';
const isActive = activeListTab === index;
return (
@@ -597,10 +550,7 @@ export const InterpretationLog: React.FC = ({ isOpen, se
}
}}
onDoubleClick={() => {
- handleStartEditListName(
- listItem.id,
- listItem.name || `List Data ${index + 1}`
- );
+ startEdit(listItem.id, 'list', listItem.name || `List Data ${index + 1}`)
}}
sx={{
px: 3,
@@ -638,15 +588,12 @@ export const InterpretationLog: React.FC = ({ isOpen, se
>
{isEditing ? (
setEditingListNameValue(e.target.value)}
- onBlur={handleSaveListName}
+ value={editing.value}
+ onChange={(e) => setEditing({ ...editing, value: e.target.value })}
+ onBlur={saveEdit}
onKeyDown={(e) => {
- if (e.key === 'Enter') handleSaveListName();
- if (e.key === 'Escape') {
- setEditingListName(null);
- setEditingListNameValue('');
- }
+ if (e.key === 'Enter') saveEdit();
+ if (e.key === 'Escape') cancelEdit();
}}
autoFocus
size="small"
@@ -842,7 +789,7 @@ export const InterpretationLog: React.FC = ({ isOpen, se
if (!screenshotStep) return null;
const isActive = activeScreenshotTab === index;
- const isEditing = editingScreenshotName === screenshotStep.id;
+ const isEditing = editing.stepId === screenshotStep.id && editing.type === 'screenshot';
const screenshotName = screenshotStep.name || `Screenshot ${index + 1}`;
return (
@@ -858,9 +805,7 @@ export const InterpretationLog: React.FC = ({ isOpen, se
setActiveScreenshotTab(index);
}
}}
- onDoubleClick={() => {
- handleStartEditScreenshotName(screenshotStep.id, screenshotName);
- }}
+ onDoubleClick={() => startEdit(screenshotStep.id, 'screenshot', screenshotName)}
sx={{
px: 3,
py: 1.25,
@@ -895,15 +840,12 @@ export const InterpretationLog: React.FC = ({ isOpen, se
>
{isEditing ? (
setEditingScreenshotNameValue(e.target.value)}
- onBlur={handleSaveScreenshotName}
+ value={editing.value}
+ onChange={(e) => setEditing({ ...editing, value: e.target.value })}
+ onBlur={saveEdit}
onKeyDown={(e) => {
- if (e.key === 'Enter') handleSaveScreenshotName();
- if (e.key === 'Escape') {
- setEditingScreenshotName(null);
- setEditingScreenshotNameValue('');
- }
+ if (e.key === 'Enter') saveEdit();
+ if (e.key === 'Escape') cancelEdit();
}}
autoFocus
size="small"
@@ -1059,7 +1001,7 @@ export const InterpretationLog: React.FC = ({ isOpen, se
{captureTextData.map((textStep: any, index) => {
- const isEditing = editingTextLabel === textStep.id;
+ const isEditing = editing.stepId === textStep.id && editing.type === 'text';
return (
= ({ isOpen, se
{isEditing ? (
setEditingTextLabelValue(e.target.value)}
- onBlur={handleSaveTextLabel}
+ value={editing.value}
+ onChange={(e) => setEditing({ ...editing, value: e.target.value })}
+ onBlur={saveEdit}
onKeyDown={(e) => {
- if (e.key === 'Enter') handleSaveTextLabel();
- if (e.key === 'Escape') handleCancelTextLabel();
+ if (e.key === 'Enter') saveEdit();
+ if (e.key === 'Escape') cancelEdit();
}}
autoFocus
size="small"
@@ -1102,7 +1044,7 @@ export const InterpretationLog: React.FC = ({ isOpen, se
/>
= ({ isOpen, se
textDecoration: 'underline'
}
}}
- onClick={() => handleStartEditTextLabel(textStep.id, textStep.label)}
+ onClick={() => startEdit(textStep.id, 'text', textStep.label)}
>
{textStep.label}
diff --git a/src/components/run/RunContent.tsx b/src/components/run/RunContent.tsx
index 4cc787d1..197f4b1f 100644
--- a/src/components/run/RunContent.tsx
+++ b/src/components/run/RunContent.tsx
@@ -615,7 +615,7 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe
return (
-
+
{row.status === 'running' || row.status === 'queued' ? (
diff --git a/src/pages/MainPage.tsx b/src/pages/MainPage.tsx
index 86178117..4f302135 100644
--- a/src/pages/MainPage.tsx
+++ b/src/pages/MainPage.tsx
@@ -1,7 +1,7 @@
import React, { useCallback, useContext, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { MainMenu } from "../components/dashboard/MainMenu";
-import { Stack } from "@mui/material";
+import { Stack, Box } from "@mui/material";
import { Recordings } from "../components/robot/Recordings";
import { Runs } from "../components/run/Runs";
import ProxyForm from '../components/proxy/ProxyForm';
@@ -11,7 +11,7 @@ import { createAndRunRecording, createRunForStoredRecording, CreateRunResponseWi
import { io, Socket } from "socket.io-client";
import { stopRecording } from "../api/recording";
import { RunSettings } from "../components/run/RunSettings";
-import { ScheduleSettings } from "../components/robot/ScheduleSettings";
+import { ScheduleSettings } from "../components/robot/pages/ScheduleSettingsPage";
import { apiUrl } from "../apiConfig";
import { useNavigate } from 'react-router-dom';
import { AuthContext } from '../context/auth';
@@ -318,12 +318,29 @@ export const MainPage = ({ handleEditRecording, initialContent }: MainPageProps)
}
}
- return (
-
-
+return (
+
+
-
+
+
+
{DisplayContent()}
-
- );
-};
+
+
+)
+}
diff --git a/src/pages/PageWrapper.tsx b/src/pages/PageWrapper.tsx
index 85bf311f..ea676300 100644
--- a/src/pages/PageWrapper.tsx
+++ b/src/pages/PageWrapper.tsx
@@ -13,6 +13,7 @@ import UserRoute from '../routes/userRoute';
import { Routes, Route, useNavigate, Navigate } from 'react-router-dom';
import { NotFoundPage } from '../components/dashboard/NotFound';
import RobotCreate from '../components/robot/pages/RobotCreate';
+import { Box } from '@mui/material';
export const PageWrapper = () => {
const [open, setOpen] = useState(false);
@@ -84,44 +85,61 @@ export const PageWrapper = () => {
}
}
}, [location.pathname, navigate, setBrowserId, setRecordingId, setRecordingName, setRecordingUrl]);
+
+ const isAuthPage = location.pathname === '/login' || location.pathname === '/register';
+ const isRecordingPage = location.pathname === '/recording';
return (
- {/* {!browserId && location.pathname !== '/recording' && } */}
- {location.pathname !== '/recording' && }
-
- }>
- } />
- } />
- } />
- } />
- } />
- } />
-
- }>
-
-
-
- } />
-
- }
- />
- }
- />
- }
- />
- } />
-
+ {/* Show NavBar only for main app pages, not for recording pages */}
+ {!isRecordingPage && (
+
+
+
+ )}
+
+
+ }>
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+
+ }>
+
+
+
+ } />
+
+ }
+ />
+ }
+ />
+ }
+ />
+ } />
+
+