From 196930ba2fef97c4e7c9c51e637ad92310ba884f Mon Sep 17 00:00:00 2001 From: Rohit Rajan Date: Tue, 9 Sep 2025 16:30:22 +0530 Subject: [PATCH 1/3] fix: rm collapse accordion on run finish --- src/components/run/ColapsibleRow.tsx | 41 +++----- src/components/run/RunsTable.tsx | 141 ++++++++++++++++++++++++--- 2 files changed, 141 insertions(+), 41 deletions(-) diff --git a/src/components/run/ColapsibleRow.tsx b/src/components/run/ColapsibleRow.tsx index d69acb8f..bf6e9c40 100644 --- a/src/components/run/ColapsibleRow.tsx +++ b/src/components/run/ColapsibleRow.tsx @@ -11,7 +11,6 @@ import { GenericModal } from "../ui/GenericModal"; import { modalStyle } from "../recorder/AddWhereCondModal"; import { getUserById } from "../../api/auth"; import { useTranslation } from "react-i18next"; -import { useNavigate } from "react-router-dom"; interface RunTypeChipProps { runByUserId?: string; @@ -22,9 +21,9 @@ interface RunTypeChipProps { const RunTypeChip: React.FC = ({ 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 2628bdda..87a0488c 100644 --- a/src/components/run/RunsTable.tsx +++ b/src/components/run/RunsTable.tsx @@ -9,7 +9,7 @@ import TableContainer from '@mui/material/TableContainer'; import TableHead from '@mui/material/TableHead'; import TablePagination from '@mui/material/TablePagination'; import TableRow from '@mui/material/TableRow'; -import { Accordion, AccordionSummary, AccordionDetails, Typography, Box, TextField, Tooltip } from '@mui/material'; +import { Accordion, AccordionSummary, AccordionDetails, Typography, Box, TextField, Tooltip, CircularProgress } from '@mui/material'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import SearchIcon from '@mui/icons-material/Search'; import { useLocation, useNavigate } from 'react-router-dom'; @@ -134,15 +134,83 @@ export const RunsTable: React.FC = ({ const [rows, setRows] = useState([]); const [searchTerm, setSearchTerm] = useState(''); + 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); }, []); @@ -224,6 +292,8 @@ export const RunsTable: React.FC = ({ } } catch (error) { notify('error', t('runstable.notifications.fetch_error')); + } finally { + setIsFetching(false); } }, [notify, t]); @@ -231,6 +301,7 @@ export const RunsTable: React.FC = ({ let mounted = true; if (rows.length === 0 || rerenderRuns) { + setIsFetching(true); fetchRuns().then(() => { if (mounted) { setRerenderRuns(false); @@ -326,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]; @@ -378,7 +450,43 @@ export const RunsTable: React.FC = ({ /> - + {isFetching ? ( + + + + ) : Object.keys(groupedRows).length === 0 ? ( + + + {searchTerm ? t('runstable.placeholder.search') : t('runstable.placeholder.title')} + + + {searchTerm + ? t('recordingtable.search_criteria') + : t('runstable.placeholder.body') + } + + + ) : ( + <> + {Object.entries(groupedRows) .slice( accordionPage * accordionsPerPage, @@ -386,12 +494,13 @@ export const RunsTable: React.FC = ({ ) .map(([robotMetaId, data]) => ( handleAccordionChange(robotMetaId, isExpanded)} TransitionProps={{ unmountOnExit: true }} // Optimize accordion rendering > }> - {data[0].name} + {data[data.length - 1].name} @@ -465,15 +574,17 @@ export const RunsTable: React.FC = ({ ))} - + + + )} ); }; \ No newline at end of file From 21b91f083da0fad2f91ad14b4a6b6f1c530d2a39 Mon Sep 17 00:00:00 2001 From: Rohit Rajan Date: Tue, 9 Sep 2025 16:41:05 +0530 Subject: [PATCH 2/3] feat: add loading spinner --- src/components/robot/RecordingsTable.tsx | 65 ++++++++++++++++++++---- 1 file changed, 54 insertions(+), 11 deletions(-) diff --git a/src/components/robot/RecordingsTable.tsx b/src/components/robot/RecordingsTable.tsx index 6ea86f74..4b0a99af 100644 --- a/src/components/robot/RecordingsTable.tsx +++ b/src/components/robot/RecordingsTable.tsx @@ -21,6 +21,7 @@ import { Menu, ListItemIcon, ListItemText, + CircularProgress, FormControlLabel, Checkbox, } from "@mui/material"; @@ -154,6 +155,7 @@ export const RecordingsTable = ({ const [searchTerm, setSearchTerm] = React.useState(''); const [isWarningModalOpen, setWarningModalOpen] = React.useState(false); const [activeBrowserId, setActiveBrowserId] = React.useState(''); + const [isFetching, setIsFetching] = React.useState(true); const columns = useMemo(() => [ { id: 'interpret', label: t('recordingtable.run'), minWidth: 80 }, @@ -270,6 +272,8 @@ export const RecordingsTable = ({ } catch (error) { console.error('Error fetching recordings:', error); notify('error', t('recordingtable.notifications.fetch_error')); + } finally { + setIsFetching(false); } }, [setRecordings, notify, t]); @@ -513,7 +517,44 @@ export const RecordingsTable = ({ - + + {isFetching ? ( + + + + ) : filteredRows.length === 0 ? ( + + + {debouncedSearchTerm ? t('recordingtable.placeholder.search') : t('recordingtable.placeholder.title')} + + + {debouncedSearchTerm + ? t('recordingtable.search_criteria') + : t('recordingtable.placeholder.body') + } + + + ) : ( + <> +
@@ -538,17 +579,19 @@ export const RecordingsTable = ({ ))}
-
+
- + + + )} setWarningModalOpen(false)} modalStyle={modalStyle}>
{t('recordingtable.warning_modal.title')} From 61775c81e83e85282101c9289f2d988b43da5014 Mon Sep 17 00:00:00 2001 From: Rohit Rajan Date: Tue, 9 Sep 2025 20:15:27 +0530 Subject: [PATCH 3/3] chore: add translations --- public/locales/de.json | 6 ++++++ public/locales/en.json | 6 ++++++ public/locales/es.json | 6 ++++++ public/locales/ja.json | 6 ++++++ public/locales/tr.json | 6 ++++++ public/locales/zh.json | 6 ++++++ 6 files changed, 36 insertions(+) diff --git a/public/locales/de.json b/public/locales/de.json index 58d90cff..91017d70 100644 --- a/public/locales/de.json +++ b/public/locales/de.json @@ -84,12 +84,18 @@ "runs": "Alle Ausführungen", "runStatus": "Status", "runName": "Name", + "name": "Name", "startedAt": "Gestartet am", "finishedAt": "Beendet am", "delete": "Löschen", "settings": "Einstellungen", "search": "Ausführungen suchen...", "sort_tooltip": "Zum Sortieren klicken", + "placeholder": { + "title": "Keine Ausführungen gefunden", + "body": "Hier werden alle Ausführungen Ihrer Roboter angezeigt. Sobald ein Roboter aktiv ist, werden seine Ausführungen hier protokolliert.", + "search": "Keine Ausführungen entsprechen Ihrer Suche" + }, "notifications": { "no_runs": "Keine Ausführungen gefunden. Bitte versuchen Sie es erneut.", "delete_success": "Ausführung erfolgreich gelöscht" diff --git a/public/locales/en.json b/public/locales/en.json index f900089f..c441761b 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -84,12 +84,18 @@ "runs":"All Runs", "runStatus":"Status", "runName":"Name", + "name":"Name", "startedAt":"Started At", "finishedAt":"Finished At", "delete":"Delete", "settings":"Settings", "search":"Search Runs...", "sort_tooltip": "Click to sort", + "placeholder": { + "title": "No Runs Found", + "body": "This is where all your robot runs will appear. Once a robot is active, its runs will be logged here.", + "search": "No runs match your search" + }, "notifications": { "no_runs": "No runs found. Please try again.", "delete_success": "Run deleted successfully" diff --git a/public/locales/es.json b/public/locales/es.json index 880f3be9..a8f5ca2d 100644 --- a/public/locales/es.json +++ b/public/locales/es.json @@ -84,12 +84,18 @@ "runs": "Todas las ejecuciones", "runStatus": "Estado", "runName": "Nombre", + "name": "Nombre", "startedAt": "Iniciado el", "finishedAt": "Finalizado el", "delete": "Eliminar", "settings": "Ajustes", "search": "Buscar ejecuciones...", "sort_tooltip": "Haga clic para ordenar", + "placeholder": { + "title": "No se encontraron ejecuciones", + "body": "Aquí aparecerán todas las ejecuciones de sus robots. Una vez que un robot esté activo, sus ejecuciones se registrarán aquí.", + "search": "Ninguna ejecución coincide con su búsqueda" + }, "notifications": { "no_runs": "No se encontraron ejecuciones. Por favor, inténtelo de nuevo.", "delete_success": "Ejecución eliminada con éxito" diff --git a/public/locales/ja.json b/public/locales/ja.json index 05071e8a..3230a12e 100644 --- a/public/locales/ja.json +++ b/public/locales/ja.json @@ -84,12 +84,18 @@ "runs": "すべての実行", "runStatus": "ステータス", "runName": "名前", + "name": "名前", "startedAt": "開始日時", "finishedAt": "終了日時", "delete": "削除", "settings": "設定", "search": "実行を検索...", "sort_tooltip": "クリックして並べ替え", + "placeholder": { + "title": "実行が見つかりません", + "body": "ここにはすべてのロボットの実行が表示されます。ロボットがアクティブになると、その実行がここに記録されます。", + "search": "あなたの検索に一致する実行はありません" + }, "notifications": { "no_runs": "実行が見つかりません。もう一度お試しください。", "delete_success": "実行が正常に削除されました" diff --git a/public/locales/tr.json b/public/locales/tr.json index db2f9ffd..09b43cdf 100644 --- a/public/locales/tr.json +++ b/public/locales/tr.json @@ -84,12 +84,18 @@ "runs": "Tüm Çalıştırmalar", "runStatus": "Durum", "runName": "Ad", + "name": "Ad", "startedAt": "Başlama", "finishedAt": "Bitiş", "delete": "Sil", "settings": "Ayarlar", "search": "Çalıştırma Ara...", "sort_tooltip": "Sıralamak için tıkla", + "placeholder": { + "title": "Çalıştırma Bulunamadı", + "body": "Tüm robot çalıştırmalarınız burada görünecek. Bir robot aktif olduğunda, çalıştırmaları burada kaydedilecek.", + "search": "Aramanızla eşleşen çalıştırma yok" + }, "notifications": { "no_runs": "Çalıştırma bulunamadı. Lütfen tekrar deneyin.", "delete_success": "Çalıştırma başarıyla silindi" diff --git a/public/locales/zh.json b/public/locales/zh.json index abd5de9f..4d39f39f 100644 --- a/public/locales/zh.json +++ b/public/locales/zh.json @@ -84,12 +84,18 @@ "runs": "所有运行记录", "runStatus": "状态", "runName": "名称", + "name": "名称", "startedAt": "开始时间", "finishedAt": "结束时间", "delete": "删除", "settings": "设置", "search": "搜索运行记录...", "sort_tooltip": "点击排序", + "placeholder": { + "title": "未找到运行记录", + "body": "这里将显示您所有机器人的运行记录。一旦机器人激活,其运行记录将记录在这里。", + "search": "没有运行记录与您的搜索匹配" + }, "notifications": { "no_runs": "未找到运行记录。请重试。", "delete_success": "运行记录删除成功"