Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion public/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
"finishedAt": "Beendet am",
"delete": "Löschen",
"settings": "Einstellungen",
"rerun": "Erneut ausführen",
"search": "Ausführungen suchen...",
"sort_tooltip": "Zum Sortieren klicken",
"placeholder": {
Expand Down Expand Up @@ -577,7 +578,8 @@
"run_by_api": "Ausgeführt durch API",
"run_type": "Ausführungstyp"
}
}
},
"rerun": "Erneut ausführen"
},
"run_content": {
"tabs": {
Expand Down
4 changes: 3 additions & 1 deletion public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
"finishedAt":"Finished At",
"delete":"Delete",
"settings":"Settings",
"rerun":"Re-run",
"search":"Search Runs...",
"sort_tooltip": "Click to sort",
"placeholder": {
Expand Down Expand Up @@ -577,7 +578,8 @@
"run_by_api": "Run by API",
"run_type": "Run Type"
}
}
},
"rerun": "Re-run"
},
"run_content": {
"tabs": {
Expand Down
4 changes: 3 additions & 1 deletion public/locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
"finishedAt": "Finalizado el",
"delete": "Eliminar",
"settings": "Ajustes",
"rerun": "Re-ejecutar",
"search": "Buscar ejecuciones...",
"sort_tooltip": "Haga clic para ordenar",
"placeholder": {
Expand Down Expand Up @@ -577,7 +578,8 @@
"run_by_api": "Ejecutado por API",
"run_type": "Tipo de Ejecución"
}
}
},
"rerun": "Re-ejecutar"
},
"run_content": {
"tabs": {
Expand Down
4 changes: 3 additions & 1 deletion public/locales/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
"finishedAt": "終了日時",
"delete": "削除",
"settings": "設定",
"rerun": "再実行",
"search": "実行を検索...",
"sort_tooltip": "クリックして並べ替え",
"placeholder": {
Expand Down Expand Up @@ -578,7 +579,8 @@
"run_by_api": "APIによる実行",
"run_type": "実行タイプ"
}
}
},
"rerun": "再実行"
},
"run_content": {
"tabs": {
Expand Down
4 changes: 3 additions & 1 deletion public/locales/tr.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
"finishedAt": "Bitiş",
"delete": "Sil",
"settings": "Ayarlar",
"rerun": "Yeniden Çalıştır",
"search": "Çalıştırma Ara...",
"sort_tooltip": "Sıralamak için tıkla",
"placeholder": {
Expand Down Expand Up @@ -578,7 +579,8 @@
"run_by_api": "API",
"run_type": "Tür"
}
}
},
"rerun": "Yeniden Çalıştır"
},
"run_content": {
"tabs": {
Expand Down
4 changes: 3 additions & 1 deletion public/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
"finishedAt": "结束时间",
"delete": "删除",
"settings": "设置",
"rerun": "重新运行",
"search": "搜索运行记录...",
"sort_tooltip": "点击排序",
"placeholder": {
Expand Down Expand Up @@ -578,7 +579,8 @@
"run_by_api": "由API运行",
"run_type": "运行类型"
}
}
},
"rerun": "重新运行"
},
"run_content": {
"tabs": {
Expand Down
25 changes: 21 additions & 4 deletions src/components/run/ColapsibleRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import * as React from "react";
import TableRow from "@mui/material/TableRow";
import TableCell from "@mui/material/TableCell";
import {
Box, Collapse, IconButton, Typography, Chip, TextField, Dialog, DialogTitle,
Box, Collapse, IconButton, Tooltip, Typography, Chip, TextField, Dialog, DialogTitle,
DialogContent,
DialogContentText,
DialogActions,
CircularProgress,
} from "@mui/material";
import { Button } from "@mui/material";
import { DeleteForever, KeyboardArrowDown, KeyboardArrowUp, Settings } from "@mui/icons-material";
import { DeleteForever, KeyboardArrowDown, KeyboardArrowUp, Replay, Settings } from "@mui/icons-material";
import { deleteRunFromStorage, getStoredRun } from "../../api/storage";
import { columns, Data } from "./RunsTable";
import { RunContent } from "./RunContent";
Expand Down Expand Up @@ -47,10 +47,11 @@ interface CollapsibleRowProps {
onToggleExpanded: (shouldExpand: boolean) => void;
currentLog: string;
abortRunHandler: (runId: string, robotName: string, browserId: string) => void;
rerunHandler: (robotMetaId: string, robotName: string, interpreterSettings: any) => void;
runningRecordingName: string;
urlRunId: string | null;
}
export const CollapsibleRow = ({ row, handleDelete, isOpen, onToggleExpanded, currentLog, abortRunHandler, runningRecordingName, urlRunId }: CollapsibleRowProps) => {
export const CollapsibleRow = ({ row, handleDelete, isOpen, onToggleExpanded, currentLog, abortRunHandler, rerunHandler, runningRecordingName, urlRunId }: CollapsibleRowProps) => {
const { t } = useTranslation();
const theme = useTheme();
const [isDeleteOpen, setDeleteOpen] = useState(false);
Expand Down Expand Up @@ -205,6 +206,22 @@ export const CollapsibleRow = ({ row, handleDelete, isOpen, onToggleExpanded, cu
{row.status === 'aborted' && <Chip label={t('runs_table.run_status_chips.aborted')} color="error" variant="outlined" />}
</TableCell>
)
case 'rerun':
return (
<TableCell key={column.id} align={column.align}>
{['success', 'failed', 'aborted'].includes(row.status) && (
<Tooltip title={t('runs_table.rerun')}>
<IconButton
aria-label={t('runs_table.rerun')}
size="small"
onClick={() => rerunHandler(row.robotMetaId, row.name, row.interpreterSettings)}
>
<Replay />
</IconButton>
</Tooltip>
)}
</TableCell>
);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
case 'delete':
return (
<TableCell key={column.id} align={column.align}>
Expand Down Expand Up @@ -297,7 +314,7 @@ export const CollapsibleRow = ({ row, handleDelete, isOpen, onToggleExpanded, cu
})}
</TableRow>
<TableRow>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={6}>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={columns.length + 1}>
<Collapse in={isOpen} timeout="auto" unmountOnExit>
{isLoadingRunDetails ? (
<Box display="flex" justifyContent="center" py={3}>
Expand Down
4 changes: 3 additions & 1 deletion src/components/run/Runs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,21 @@ import { RunsTable } from "./RunsTable";
interface RunsProps {
currentInterpretationLog: string;
abortRunHandler: (runId: string, robotName: string, browserId: string) => void;
rerunHandler: (robotMetaId: string, robotName: string, interpreterSettings: any) => void;
runId: string;
runningRecordingName: string;
}

export const Runs = (
{ currentInterpretationLog, abortRunHandler, runId, runningRecordingName }: RunsProps) => {
{ currentInterpretationLog, abortRunHandler, rerunHandler, runId, runningRecordingName }: RunsProps) => {

return (
<Grid container direction="column" sx={{ padding: '30px' }}>
<Grid item xs>
<RunsTable
currentInterpretationLog={currentInterpretationLog}
abortRunHandler={abortRunHandler}
rerunHandler={rerunHandler}
runId={runId}
runningRecordingName={runningRecordingName}
/>
Expand Down
8 changes: 6 additions & 2 deletions src/components/run/RunsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const columns: readonly Column[] = [
{ id: 'startedAt', label: 'Started At', minWidth: 80 },
{ id: 'finishedAt', label: 'Finished At', minWidth: 80 },
{ id: 'settings', label: 'Settings', minWidth: 80 },
{ id: 'rerun', label: 'Re-run', minWidth: 80 },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix i18n namespace mismatch for the new Re-run column header.

The column is translated via runstable.rerun, but this PR adds runs_table.rerun; in non-English locales the header will fall back to "Re-run" instead of using localized text.

🔧 Proposed fix
 export const columns: readonly Column[] = [
   { id: 'runStatus', label: 'Status', minWidth: 80 },
   { id: 'name', label: 'Name', minWidth: 80 },
   { id: 'startedAt', label: 'Started At', minWidth: 80 },
   { id: 'finishedAt', label: 'Finished At', minWidth: 80 },
   { id: 'settings', label: 'Settings', minWidth: 80 },
   { id: 'rerun', label: 'Re-run', minWidth: 80 },
   { id: 'delete', label: 'Delete', minWidth: 80 },
 ];

 const translatedColumns = useMemo(() => 
   columns.map(column => ({
     ...column,
-    label: t(`runstable.${column.id}`, column.label)
+    label: t(
+      column.id === 'rerun' ? 'runs_table.rerun' : `runstable.${column.id}`,
+      column.label
+    )
   })),
   [t]
 );

Also applies to: 132-136

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/run/RunsTable.tsx` at line 29, The new "Re-run" column in
RunsTable uses the wrong i18n key namespace (it references runs_table.rerun)
causing fallbacks; update the column definition with id 'rerun' and any usages
(e.g., the header label lookup and the cell rendering around the rerun column)
to use the existing translation key runstable.rerun consistently (or
alternatively add runs_table.rerun to the locales), ensuring both the column
descriptor and the render code reference the same key.

{ id: 'delete', label: 'Delete', minWidth: 80 },
];

Expand All @@ -39,7 +40,7 @@ interface AccordionSortConfig {
}

interface Column {
id: 'runStatus' | 'name' | 'startedAt' | 'finishedAt' | 'delete' | 'settings';
id: 'runStatus' | 'name' | 'startedAt' | 'finishedAt' | 'delete' | 'settings' | 'rerun';
label: string;
minWidth?: number;
align?: 'right';
Expand Down Expand Up @@ -71,6 +72,7 @@ export interface Data {
interface RunsTableProps {
currentInterpretationLog: string;
abortRunHandler: (runId: string, robotName: string, browserId: string) => void;
rerunHandler: (robotMetaId: string, robotName: string, interpreterSettings: any) => void;
runId: string;
runningRecordingName: string;
}
Expand All @@ -85,6 +87,7 @@ interface PaginationState {
export const RunsTable: React.FC<RunsTableProps> = ({
currentInterpretationLog,
abortRunHandler,
rerunHandler,
runId,
runningRecordingName
}) => {
Expand Down Expand Up @@ -472,11 +475,12 @@ export const RunsTable: React.FC<RunsTableProps> = ({
onToggleExpanded={(shouldExpand) => handleRowExpand(row.runId, row.robotMetaId, shouldExpand)}
currentLog={currentInterpretationLog}
abortRunHandler={abortRunHandler}
rerunHandler={rerunHandler}
runningRecordingName={runningRecordingName}
urlRunId={urlRunId}
/>
));
}, [paginationStates, runId, runningRecordingName, currentInterpretationLog, abortRunHandler, handleDelete, accordionSortConfigs]);
}, [paginationStates, runId, runningRecordingName, currentInterpretationLog, abortRunHandler, rerunHandler, handleDelete, accordionSortConfigs, expandedRows, handleRowExpand, getPaginationState, urlRunId]);

const renderSortIcon = useCallback((column: Column, robotMetaId: string) => {
const sortConfig = accordionSortConfigs[robotMetaId];
Expand Down
57 changes: 57 additions & 0 deletions src/pages/MainPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,62 @@ export const MainPage = ({ handleEditRecording, initialContent }: MainPageProps)
};
}, []);

const handleReRunRecording = useCallback((robotMetaId: string, robotName: string, interpreterSettings: RunSettings) => {
createAndRunRecording(robotMetaId, interpreterSettings).then((response: CreateRunResponseWithQueue) => {
invalidateRuns();
const { browserId, runId, queued } = response;

if (!runId) {
notify('error', t('main_page.notifications.run_start_failed', { name: robotName }));
return;
}

navigate(`/runs/${robotMetaId}/run/${runId}`);

if (queued) {
setQueuedRuns(prev => new Set([...prev, runId]));
notify('info', `Run queued: ${robotName}`);
Comment on lines +261 to +263

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Localize the queued rerun toast.

This branch still emits a hardcoded English notification, so the rerun flow is only partially translated even though this PR adds locale coverage for the feature. Please move this behind an i18n key and add the matching locale entries.

Suggested change
-        notify('info', `Run queued: ${robotName}`);
+        notify('info', t('main_page.notifications.run_queued', { name: robotName }));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (queued) {
setQueuedRuns(prev => new Set([...prev, runId]));
notify('info', `Run queued: ${robotName}`);
if (queued) {
setQueuedRuns(prev => new Set([...prev, runId]));
notify('info', t('main_page.notifications.run_queued', { name: robotName }));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/MainPage.tsx` around lines 261 - 263, The toast for queued reruns
is hardcoded in English; update the notify call inside the queued branch (where
queued is truthy and setQueuedRuns is called with runId) to use an i18n lookup
instead of the literal string (reference notify(..., `Run queued: ${robotName}`)
in MainPage.tsx), and add the corresponding locale key/value entries to the
translation files used by the app so all supported locales show the translated
message that can interpolate robotName. Ensure you call the translation helper
the project uses (e.g., t or i18n.t) and pass robotName as the interpolation
parameter.

} else {
if (!browserId) {
notify('error', t('main_page.notifications.run_start_failed', { name: robotName }));
return;
Comment on lines +264 to +267

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle successful reruns that do not allocate a browser.

CreateRunResponse.browserId is nullable in this file, and handleRunRecording already treats browserId === null as a valid response. This rerun path turns the same case into run_start_failed, so browserless/immediate reruns will show a failure toast after a real runId was created and Line 259 already navigated to it.

Suggested change
-      } else {
-        if (!browserId) {
-          notify('error', t('main_page.notifications.run_start_failed', { name: robotName }));
-          return;
-        }
-
+      } else if (browserId) {
         const socket = io(`${apiUrl}/${browserId}`, {
           transports: ["websocket", "polling"],
           rejectUnauthorized: false
         });
 
         setSockets(sockets => [...sockets, socket]);
@@
 
         notify('info', t('main_page.notifications.run_started', { name: robotName }));
+      } else {
+        notify('info', t('main_page.notifications.run_started', { name: robotName }));
       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} else {
if (!browserId) {
notify('error', t('main_page.notifications.run_start_failed', { name: robotName }));
return;
} else if (browserId) {
const socket = io(`${apiUrl}/${browserId}`, {
transports: ["websocket", "polling"],
rejectUnauthorized: false
});
setSockets(sockets => [...sockets, socket]);
socket.on('run-completed', (data) => {
setRerenderRuns(true);
invalidateRuns();
if (data.status === 'success') {
notify('success', t('main_page.notifications.interpretation_success', { name: robotName }));
} else {
notify('error', t('main_page.notifications.interpretation_failed', { name: robotName }));
}
});
socket.on('connect_error', (error) => {
console.log('error', `Failed to connect to browser ${browserId}: ${error}`);
notify('error', t('main_page.notifications.connection_failed', { name: robotName }));
});
socket.on('disconnect', (reason) => {
console.log('warn', `Disconnected from browser ${browserId}: ${reason}`);
});
notify('info', t('main_page.notifications.run_started', { name: robotName }));
} else {
notify('info', t('main_page.notifications.run_started', { name: robotName }));
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/MainPage.tsx` around lines 264 - 267, The rerun path incorrectly
treats CreateRunResponse.browserId === null as a failure and shows
notify('error', t('main_page.notifications.run_start_failed', { name: robotName
})); but handleRunRecording and other logic already treat browserId === null as
a valid browserless run; change the condition so it only errors when there is no
runId (or browserId is undefined/unset) rather than when browserId === null —
e.g., check response.runId (or use strict undefined check for browserId) and
remove the notify/return for the null-browser case so browserless reruns proceed
to handleRunRecording/navigation as intended (refer to
CreateRunResponse.browserId, handleRunRecording, notify, run_start_failed,
robotName).

}

const socket = io(`${apiUrl}/${browserId}`, {
transports: ["websocket", "polling"],
rejectUnauthorized: false
});

setSockets(sockets => [...sockets, socket]);

socket.on('run-completed', (data) => {
setRerenderRuns(true);
invalidateRuns();
if (data.status === 'success') {
notify('success', t('main_page.notifications.interpretation_success', { name: robotName }));
} else {
notify('error', t('main_page.notifications.interpretation_failed', { name: robotName }));
}
});

socket.on('connect_error', (error) => {
console.log('error', `Failed to connect to browser ${browserId}: ${error}`);
});

socket.on('disconnect', (reason) => {
console.log('warn', `Disconnected from browser ${browserId}: ${reason}`);
});

notify('info', t('main_page.notifications.run_started', { name: robotName }));
}

setContent('runs');
}).catch((error: any) => {
notify('error', t('main_page.notifications.run_start_failed', { name: robotName }));
console.error('Error in rerun:', error);
});
}, [navigate, notify, t, invalidateRuns, setRerenderRuns, setQueuedRuns, setSockets, setContent]);

const handleScheduleRecording = async (settings: ScheduleSettings) => {
const { message, runId }: ScheduleRunResponse = await scheduleStoredRecording(runningRecordingId, settings);
if (message === 'success') {
Expand Down Expand Up @@ -330,6 +386,7 @@ export const MainPage = ({ handleEditRecording, initialContent }: MainPageProps)
return <Runs
currentInterpretationLog={currentInterpretationLog}
abortRunHandler={abortRunHandler}
rerunHandler={handleReRunRecording}
runId={ids.runId}
runningRecordingName={runningRecordingName}
/>;
Expand Down