diff --git a/public/locales/de.json b/public/locales/de.json
index 9a621016..12dba104 100644
--- a/public/locales/de.json
+++ b/public/locales/de.json
@@ -90,6 +90,7 @@
"runs": "Alle Ausführungen",
"runStatus": "Status",
"runName": "Name",
+ "name": "Name",
"startedAt": "Gestartet am",
"finishedAt": "Beendet am",
"delete": "Löschen",
diff --git a/public/locales/en.json b/public/locales/en.json
index 025e28b4..c9f218d8 100644
--- a/public/locales/en.json
+++ b/public/locales/en.json
@@ -90,6 +90,7 @@
"runs":"All Runs",
"runStatus":"Status",
"runName":"Name",
+ "name":"Name",
"startedAt":"Started At",
"finishedAt":"Finished At",
"delete":"Delete",
diff --git a/public/locales/es.json b/public/locales/es.json
index d266565c..47ce46e3 100644
--- a/public/locales/es.json
+++ b/public/locales/es.json
@@ -90,6 +90,7 @@
"runs": "Todas las ejecuciones",
"runStatus": "Estado",
"runName": "Nombre",
+ "name": "Nombre",
"startedAt": "Iniciado el",
"finishedAt": "Finalizado el",
"delete": "Eliminar",
diff --git a/public/locales/ja.json b/public/locales/ja.json
index 78613113..69aa522f 100644
--- a/public/locales/ja.json
+++ b/public/locales/ja.json
@@ -90,6 +90,7 @@
"runs": "すべての実行",
"runStatus": "ステータス",
"runName": "名前",
+ "name": "名前",
"startedAt": "開始日時",
"finishedAt": "終了日時",
"delete": "削除",
diff --git a/public/locales/tr.json b/public/locales/tr.json
index 4fdf6d4a..b329075d 100644
--- a/public/locales/tr.json
+++ b/public/locales/tr.json
@@ -90,6 +90,7 @@
"runs": "Tüm Çalıştırmalar",
"runStatus": "Durum",
"runName": "Ad",
+ "name": "Ad",
"startedAt": "Başlama",
"finishedAt": "Bitiş",
"delete": "Sil",
diff --git a/public/locales/zh.json b/public/locales/zh.json
index a9570ea6..20769b0a 100644
--- a/public/locales/zh.json
+++ b/public/locales/zh.json
@@ -90,6 +90,7 @@
"runs": "所有运行记录",
"runStatus": "状态",
"runName": "名称",
+ "name": "名称",
"startedAt": "开始时间",
"finishedAt": "结束时间",
"delete": "删除",
diff --git a/src/components/robot/RecordingsTable.tsx b/src/components/robot/RecordingsTable.tsx
index 8e294464..ca36bd6b 100644
--- a/src/components/robot/RecordingsTable.tsx
+++ b/src/components/robot/RecordingsTable.tsx
@@ -21,6 +21,7 @@ import {
Menu,
ListItemIcon,
ListItemText,
+ CircularProgress,
FormControlLabel,
Checkbox,
CircularProgress,
@@ -155,7 +156,7 @@ export const RecordingsTable = ({
const [searchTerm, setSearchTerm] = React.useState('');
const [isWarningModalOpen, setWarningModalOpen] = React.useState(false);
const [activeBrowserId, setActiveBrowserId] = React.useState('');
- const [isLoading, setIsLoading] = React.useState(true);
+ const [isFetching, setIsFetching] = React.useState(true);
const columns = useMemo(() => [
{ id: 'interpret', label: t('recordingtable.run'), minWidth: 80 },
@@ -273,7 +274,7 @@ export const RecordingsTable = ({
console.error('Error fetching recordings:', error);
notify('error', t('recordingtable.notifications.fetch_error'));
} finally {
- setIsLoading(false);
+ setIsFetching(false);
}
}, [setRecordings, notify, t]);
@@ -515,8 +516,8 @@ export const RecordingsTable = ({
-
- {isLoading ? (
+
+ {isFetching ? (
= ({ runByUserId, runByScheduledId, runByAPI }) => {
const { t } = useTranslation();
- if (runByUserId) return ;
if (runByScheduledId) return ;
if (runByAPI) return ;
+ if (runByUserId) return ;
return ;
};
@@ -32,21 +31,20 @@ interface CollapsibleRowProps {
row: Data;
handleDelete: () => void;
isOpen: boolean;
+ onToggleExpanded: (shouldExpand: boolean) => void;
currentLog: string;
abortRunHandler: (runId: string, robotName: string, browserId: string) => void;
runningRecordingName: string;
urlRunId: string | null;
}
-export const CollapsibleRow = ({ row, handleDelete, isOpen, currentLog, abortRunHandler, runningRecordingName, urlRunId }: CollapsibleRowProps) => {
+export const CollapsibleRow = ({ row, handleDelete, isOpen, onToggleExpanded, currentLog, abortRunHandler, runningRecordingName, urlRunId }: CollapsibleRowProps) => {
const { t } = useTranslation();
- const navigate = useNavigate();
- const [open, setOpen] = useState(isOpen);
const [openSettingsModal, setOpenSettingsModal] = useState(false);
const [userEmail, setUserEmail] = useState(null);
- const runByLabel = row.runByUserId
- ? `${userEmail}`
- : row.runByScheduleId
- ? `${row.runByScheduleId}`
+ const runByLabel = row.runByScheduleId
+ ? `${row.runByScheduleId}`
+ : row.runByUserId
+ ? `${userEmail}`
: row.runByAPI
? 'API'
: 'Unknown';
@@ -63,18 +61,9 @@ export const CollapsibleRow = ({ row, handleDelete, isOpen, currentLog, abortRun
abortRunHandler(row.runId, row.name, row.browserId);
}
- useEffect(() => {
- setOpen(urlRunId === row.runId || isOpen);
- }, [urlRunId, row.runId, isOpen]);
-
const handleRowExpand = () => {
- const newOpen = !open;
- setOpen(newOpen);
- navigate(
- newOpen
- ? `/runs/${row.robotMetaId}/run/${row.runId}`
- : `/runs/${row.robotMetaId}`
- );
+ const newOpen = !isOpen;
+ onToggleExpanded(newOpen);
//scrollToLogBottom();
};
@@ -103,7 +92,7 @@ export const CollapsibleRow = ({ row, handleDelete, isOpen, currentLog, abortRun
size="small"
onClick={handleRowExpand}
>
- {open ? : }
+ {isOpen ? : }
{columns.map((column) => {
@@ -165,10 +154,10 @@ export const CollapsibleRow = ({ row, handleDelete, isOpen, currentLog, abortRun
/>
-
+
diff --git a/src/components/run/RunsTable.tsx b/src/components/run/RunsTable.tsx
index 12b2c84b..cfcb05d4 100644
--- a/src/components/run/RunsTable.tsx
+++ b/src/components/run/RunsTable.tsx
@@ -134,16 +134,83 @@ export const RunsTable: React.FC = ({
const [rows, setRows] = useState([]);
const [searchTerm, setSearchTerm] = useState('');
- const [isLoading, setIsLoading] = useState(true);
+ const [isFetching, setIsFetching] = useState(true);
const [paginationStates, setPaginationStates] = useState({});
+ const [expandedRows, setExpandedRows] = useState>(new Set());
+ const [expandedAccordions, setExpandedAccordions] = useState>(new Set());
const { notify, rerenderRuns, setRerenderRuns } = useGlobalInfoStore();
const handleAccordionChange = useCallback((robotMetaId: string, isExpanded: boolean) => {
+ setExpandedAccordions(prev => {
+ const newSet = new Set(prev);
+ if (isExpanded) {
+ newSet.add(robotMetaId);
+ } else {
+ newSet.delete(robotMetaId);
+ }
+ return newSet;
+ });
+
navigate(isExpanded ? `/runs/${robotMetaId}` : '/runs');
}, [navigate]);
+ const handleRowExpand = useCallback((runId: string, robotMetaId: string, shouldExpand: boolean) => {
+ setExpandedRows(prev => {
+ const newSet = new Set(prev);
+ if (shouldExpand) {
+ newSet.add(runId);
+ } else {
+ newSet.delete(runId);
+ }
+ return newSet;
+ });
+
+ // Update URL navigation
+ navigate(
+ shouldExpand
+ ? `/runs/${robotMetaId}/run/${runId}`
+ : `/runs/${robotMetaId}`
+ );
+ }, [navigate]);
+
+ // Sync expandedRows and expandedAccordions with URL params
+ useEffect(() => {
+ if (urlRunId) {
+ setExpandedRows(prev => {
+ const newSet = new Set(prev);
+ newSet.add(urlRunId);
+ return newSet;
+ });
+ }
+
+ if (urlRobotMetaId) {
+ setExpandedAccordions(prev => {
+ const newSet = new Set(prev);
+ newSet.add(urlRobotMetaId);
+ return newSet;
+ });
+ }
+ }, [urlRunId, urlRobotMetaId]);
+
+ // Auto-expand currently running robot (but allow manual collapse)
+ useEffect(() => {
+ if (runId && runningRecordingName) {
+ const currentRunningRow = rows.find(row =>
+ row.runId === runId && row.name === runningRecordingName
+ );
+
+ if (currentRunningRow) {
+ setExpandedRows(prev => {
+ const newSet = new Set(prev);
+ newSet.add(currentRunningRow.runId);
+ return newSet;
+ });
+ }
+ }
+ }, [runId, runningRecordingName, rows]);
+
const handleAccordionPageChange = useCallback((event: unknown, newPage: number) => {
setAccordionPage(newPage);
}, []);
@@ -226,7 +293,7 @@ export const RunsTable: React.FC = ({
} catch (error) {
notify('error', t('runstable.notifications.fetch_error'));
} finally {
- setIsLoading(false);
+ setIsFetching(false);
}
}, [notify, t]);
@@ -234,7 +301,7 @@ export const RunsTable: React.FC = ({
let mounted = true;
if (rows.length === 0 || rerenderRuns) {
- setIsLoading(true);
+ setIsFetching(true);
fetchRuns().then(() => {
if (mounted) {
setRerenderRuns(false);
@@ -330,14 +397,15 @@ export const RunsTable: React.FC = ({
key={`row-${row.id}`}
row={row}
handleDelete={handleDelete}
- isOpen={urlRunId === row.runId || (runId === row.runId && runningRecordingName === row.name)}
+ isOpen={expandedRows.has(row.runId)}
+ onToggleExpanded={(shouldExpand) => handleRowExpand(row.runId, row.robotMetaId, shouldExpand)}
currentLog={currentInterpretationLog}
abortRunHandler={abortRunHandler}
runningRecordingName={runningRecordingName}
urlRunId={urlRunId}
/>
));
- }, [paginationStates, runId, runningRecordingName, currentInterpretationLog, abortRunHandler, handleDelete, accordionSortConfigs]);
+ }, [getPaginationState, accordionSortConfigs, expandedRows, handleRowExpand, handleDelete, currentInterpretationLog, abortRunHandler, runningRecordingName, urlRunId]);
const renderSortIcon = useCallback((column: Column, robotMetaId: string) => {
const sortConfig = accordionSortConfigs[robotMetaId];
@@ -382,7 +450,7 @@ export const RunsTable: React.FC = ({
/>
- {isLoading? (
+ {isFetching ? (
= ({
)
.map(([robotMetaId, data]) => (
handleAccordionChange(robotMetaId, isExpanded)}
TransitionProps={{ unmountOnExit: true }} // Optimize accordion rendering
>
}>
- {data[0].name}
+ {data[data.length - 1].name}