Merge pull request #773 from getmaxun/accord-fix

fix: accordion collapses on run completion
This commit is contained in:
Karishma Shukla
2025-09-10 12:48:19 +05:30
committed by GitHub
9 changed files with 103 additions and 38 deletions

View File

@@ -90,6 +90,7 @@
"runs": "Alle Ausführungen", "runs": "Alle Ausführungen",
"runStatus": "Status", "runStatus": "Status",
"runName": "Name", "runName": "Name",
"name": "Name",
"startedAt": "Gestartet am", "startedAt": "Gestartet am",
"finishedAt": "Beendet am", "finishedAt": "Beendet am",
"delete": "Löschen", "delete": "Löschen",

View File

@@ -90,6 +90,7 @@
"runs":"All Runs", "runs":"All Runs",
"runStatus":"Status", "runStatus":"Status",
"runName":"Name", "runName":"Name",
"name":"Name",
"startedAt":"Started At", "startedAt":"Started At",
"finishedAt":"Finished At", "finishedAt":"Finished At",
"delete":"Delete", "delete":"Delete",

View File

@@ -90,6 +90,7 @@
"runs": "Todas las ejecuciones", "runs": "Todas las ejecuciones",
"runStatus": "Estado", "runStatus": "Estado",
"runName": "Nombre", "runName": "Nombre",
"name": "Nombre",
"startedAt": "Iniciado el", "startedAt": "Iniciado el",
"finishedAt": "Finalizado el", "finishedAt": "Finalizado el",
"delete": "Eliminar", "delete": "Eliminar",

View File

@@ -90,6 +90,7 @@
"runs": "すべての実行", "runs": "すべての実行",
"runStatus": "ステータス", "runStatus": "ステータス",
"runName": "名前", "runName": "名前",
"name": "名前",
"startedAt": "開始日時", "startedAt": "開始日時",
"finishedAt": "終了日時", "finishedAt": "終了日時",
"delete": "削除", "delete": "削除",

View File

@@ -90,6 +90,7 @@
"runs": "Tüm Çalıştırmalar", "runs": "Tüm Çalıştırmalar",
"runStatus": "Durum", "runStatus": "Durum",
"runName": "Ad", "runName": "Ad",
"name": "Ad",
"startedAt": "Başlama", "startedAt": "Başlama",
"finishedAt": "Bitiş", "finishedAt": "Bitiş",
"delete": "Sil", "delete": "Sil",

View File

@@ -90,6 +90,7 @@
"runs": "所有运行记录", "runs": "所有运行记录",
"runStatus": "状态", "runStatus": "状态",
"runName": "名称", "runName": "名称",
"name": "名称",
"startedAt": "开始时间", "startedAt": "开始时间",
"finishedAt": "结束时间", "finishedAt": "结束时间",
"delete": "删除", "delete": "删除",

View File

@@ -21,6 +21,7 @@ import {
Menu, Menu,
ListItemIcon, ListItemIcon,
ListItemText, ListItemText,
CircularProgress,
FormControlLabel, FormControlLabel,
Checkbox, Checkbox,
CircularProgress, CircularProgress,
@@ -155,7 +156,7 @@ export const RecordingsTable = ({
const [searchTerm, setSearchTerm] = React.useState(''); const [searchTerm, setSearchTerm] = React.useState('');
const [isWarningModalOpen, setWarningModalOpen] = React.useState(false); const [isWarningModalOpen, setWarningModalOpen] = React.useState(false);
const [activeBrowserId, setActiveBrowserId] = React.useState(''); const [activeBrowserId, setActiveBrowserId] = React.useState('');
const [isLoading, setIsLoading] = React.useState(true); const [isFetching, setIsFetching] = React.useState(true);
const columns = useMemo(() => [ const columns = useMemo(() => [
{ id: 'interpret', label: t('recordingtable.run'), minWidth: 80 }, { id: 'interpret', label: t('recordingtable.run'), minWidth: 80 },
@@ -273,7 +274,7 @@ export const RecordingsTable = ({
console.error('Error fetching recordings:', error); console.error('Error fetching recordings:', error);
notify('error', t('recordingtable.notifications.fetch_error')); notify('error', t('recordingtable.notifications.fetch_error'));
} finally { } finally {
setIsLoading(false); setIsFetching(false);
} }
}, [setRecordings, notify, t]); }, [setRecordings, notify, t]);
@@ -515,8 +516,8 @@ export const RecordingsTable = ({
</IconButton> </IconButton>
</Box> </Box>
</Box> </Box>
{isLoading ? ( {isFetching ? (
<Box <Box
display="flex" display="flex"
justifyContent="center" justifyContent="center"

View File

@@ -11,7 +11,6 @@ import { GenericModal } from "../ui/GenericModal";
import { modalStyle } from "../recorder/AddWhereCondModal"; import { modalStyle } from "../recorder/AddWhereCondModal";
import { getUserById } from "../../api/auth"; import { getUserById } from "../../api/auth";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
interface RunTypeChipProps { interface RunTypeChipProps {
runByUserId?: string; runByUserId?: string;
@@ -22,9 +21,9 @@ interface RunTypeChipProps {
const RunTypeChip: React.FC<RunTypeChipProps> = ({ runByUserId, runByScheduledId, runByAPI }) => { const RunTypeChip: React.FC<RunTypeChipProps> = ({ runByUserId, runByScheduledId, runByAPI }) => {
const { t } = useTranslation(); const { t } = useTranslation();
if (runByUserId) return <Chip label={t('runs_table.run_type_chips.manual_run')} color="primary" variant="outlined" />;
if (runByScheduledId) return <Chip label={t('runs_table.run_type_chips.scheduled_run')} color="primary" variant="outlined" />; if (runByScheduledId) return <Chip label={t('runs_table.run_type_chips.scheduled_run')} color="primary" variant="outlined" />;
if (runByAPI) return <Chip label={t('runs_table.run_type_chips.api')} color="primary" variant="outlined" />; if (runByAPI) return <Chip label={t('runs_table.run_type_chips.api')} color="primary" variant="outlined" />;
if (runByUserId) return <Chip label={t('runs_table.run_type_chips.manual_run')} color="primary" variant="outlined" />;
return <Chip label={t('runs_table.run_type_chips.unknown_run_type')} color="primary" variant="outlined" />; return <Chip label={t('runs_table.run_type_chips.unknown_run_type')} color="primary" variant="outlined" />;
}; };
@@ -32,21 +31,20 @@ interface CollapsibleRowProps {
row: Data; row: Data;
handleDelete: () => void; handleDelete: () => void;
isOpen: boolean; isOpen: boolean;
onToggleExpanded: (shouldExpand: boolean) => void;
currentLog: string; currentLog: string;
abortRunHandler: (runId: string, robotName: string, browserId: string) => void; abortRunHandler: (runId: string, robotName: string, browserId: string) => void;
runningRecordingName: string; runningRecordingName: string;
urlRunId: string | null; 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 { t } = useTranslation();
const navigate = useNavigate();
const [open, setOpen] = useState(isOpen);
const [openSettingsModal, setOpenSettingsModal] = useState(false); const [openSettingsModal, setOpenSettingsModal] = useState(false);
const [userEmail, setUserEmail] = useState<string | null>(null); const [userEmail, setUserEmail] = useState<string | null>(null);
const runByLabel = row.runByUserId const runByLabel = row.runByScheduleId
? `${userEmail}` ? `${row.runByScheduleId}`
: row.runByScheduleId : row.runByUserId
? `${row.runByScheduleId}` ? `${userEmail}`
: row.runByAPI : row.runByAPI
? 'API' ? 'API'
: 'Unknown'; : 'Unknown';
@@ -63,18 +61,9 @@ export const CollapsibleRow = ({ row, handleDelete, isOpen, currentLog, abortRun
abortRunHandler(row.runId, row.name, row.browserId); abortRunHandler(row.runId, row.name, row.browserId);
} }
useEffect(() => {
setOpen(urlRunId === row.runId || isOpen);
}, [urlRunId, row.runId, isOpen]);
const handleRowExpand = () => { const handleRowExpand = () => {
const newOpen = !open; const newOpen = !isOpen;
setOpen(newOpen); onToggleExpanded(newOpen);
navigate(
newOpen
? `/runs/${row.robotMetaId}/run/${row.runId}`
: `/runs/${row.robotMetaId}`
);
//scrollToLogBottom(); //scrollToLogBottom();
}; };
@@ -103,7 +92,7 @@ export const CollapsibleRow = ({ row, handleDelete, isOpen, currentLog, abortRun
size="small" size="small"
onClick={handleRowExpand} onClick={handleRowExpand}
> >
{open ? <KeyboardArrowUp /> : <KeyboardArrowDown />} {isOpen ? <KeyboardArrowUp /> : <KeyboardArrowDown />}
</IconButton> </IconButton>
</TableCell> </TableCell>
{columns.map((column) => { {columns.map((column) => {
@@ -165,10 +154,10 @@ export const CollapsibleRow = ({ row, handleDelete, isOpen, currentLog, abortRun
/> />
<TextField <TextField
label={ label={
row.runByUserId row.runByScheduleId
? t('runs_table.run_settings_modal.labels.run_by_user') ? t('runs_table.run_settings_modal.labels.run_by_schedule')
: row.runByScheduleId : row.runByUserId
? t('runs_table.run_settings_modal.labels.run_by_schedule') ? t('runs_table.run_settings_modal.labels.run_by_user')
: t('runs_table.run_settings_modal.labels.run_by_api') : t('runs_table.run_settings_modal.labels.run_by_api')
} }
value={runByLabel} value={runByLabel}
@@ -197,7 +186,7 @@ export const CollapsibleRow = ({ row, handleDelete, isOpen, currentLog, abortRun
</TableRow> </TableRow>
<TableRow> <TableRow>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={6}> <TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={6}>
<Collapse in={open} timeout="auto" unmountOnExit> <Collapse in={isOpen} timeout="auto" unmountOnExit>
<RunContent row={row} abortRunHandler={handleAbort} currentLog={currentLog} <RunContent row={row} abortRunHandler={handleAbort} currentLog={currentLog}
logEndRef={logEndRef} interpretationInProgress={runningRecordingName === row.name} /> logEndRef={logEndRef} interpretationInProgress={runningRecordingName === row.name} />
</Collapse> </Collapse>

View File

@@ -134,16 +134,83 @@ export const RunsTable: React.FC<RunsTableProps> = ({
const [rows, setRows] = useState<Data[]>([]); const [rows, setRows] = useState<Data[]>([]);
const [searchTerm, setSearchTerm] = useState(''); const [searchTerm, setSearchTerm] = useState('');
const [isLoading, setIsLoading] = useState(true); const [isFetching, setIsFetching] = useState(true);
const [paginationStates, setPaginationStates] = useState<PaginationState>({}); const [paginationStates, setPaginationStates] = useState<PaginationState>({});
const [expandedRows, setExpandedRows] = useState<Set<string>>(new Set());
const [expandedAccordions, setExpandedAccordions] = useState<Set<string>>(new Set());
const { notify, rerenderRuns, setRerenderRuns } = useGlobalInfoStore(); const { notify, rerenderRuns, setRerenderRuns } = useGlobalInfoStore();
const handleAccordionChange = useCallback((robotMetaId: string, isExpanded: boolean) => { 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(isExpanded ? `/runs/${robotMetaId}` : '/runs');
}, [navigate]); }, [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) => { const handleAccordionPageChange = useCallback((event: unknown, newPage: number) => {
setAccordionPage(newPage); setAccordionPage(newPage);
}, []); }, []);
@@ -226,7 +293,7 @@ export const RunsTable: React.FC<RunsTableProps> = ({
} catch (error) { } catch (error) {
notify('error', t('runstable.notifications.fetch_error')); notify('error', t('runstable.notifications.fetch_error'));
} finally { } finally {
setIsLoading(false); setIsFetching(false);
} }
}, [notify, t]); }, [notify, t]);
@@ -234,7 +301,7 @@ export const RunsTable: React.FC<RunsTableProps> = ({
let mounted = true; let mounted = true;
if (rows.length === 0 || rerenderRuns) { if (rows.length === 0 || rerenderRuns) {
setIsLoading(true); setIsFetching(true);
fetchRuns().then(() => { fetchRuns().then(() => {
if (mounted) { if (mounted) {
setRerenderRuns(false); setRerenderRuns(false);
@@ -330,14 +397,15 @@ export const RunsTable: React.FC<RunsTableProps> = ({
key={`row-${row.id}`} key={`row-${row.id}`}
row={row} row={row}
handleDelete={handleDelete} 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} currentLog={currentInterpretationLog}
abortRunHandler={abortRunHandler} abortRunHandler={abortRunHandler}
runningRecordingName={runningRecordingName} runningRecordingName={runningRecordingName}
urlRunId={urlRunId} 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 renderSortIcon = useCallback((column: Column, robotMetaId: string) => {
const sortConfig = accordionSortConfigs[robotMetaId]; const sortConfig = accordionSortConfigs[robotMetaId];
@@ -382,7 +450,7 @@ export const RunsTable: React.FC<RunsTableProps> = ({
/> />
</Box> </Box>
{isLoading? ( {isFetching ? (
<Box <Box
display="flex" display="flex"
justifyContent="center" justifyContent="center"
@@ -426,12 +494,13 @@ export const RunsTable: React.FC<RunsTableProps> = ({
) )
.map(([robotMetaId, data]) => ( .map(([robotMetaId, data]) => (
<Accordion <Accordion
key={robotMetaId} key={robotMetaId}
expanded={expandedAccordions.has(robotMetaId)}
onChange={(event, isExpanded) => handleAccordionChange(robotMetaId, isExpanded)} onChange={(event, isExpanded) => handleAccordionChange(robotMetaId, isExpanded)}
TransitionProps={{ unmountOnExit: true }} // Optimize accordion rendering TransitionProps={{ unmountOnExit: true }} // Optimize accordion rendering
> >
<AccordionSummary expandIcon={<ExpandMoreIcon />}> <AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography variant="h6">{data[0].name}</Typography> <Typography variant="h6">{data[data.length - 1].name}</Typography>
</AccordionSummary> </AccordionSummary>
<AccordionDetails> <AccordionDetails>
<Table stickyHeader aria-label="sticky table"> <Table stickyHeader aria-label="sticky table">