Merge pull request #761 from RohitR311/norob-ui

feat: placeholder message for empty robots and runs
This commit is contained in:
Karishma Shukla
2025-09-10 12:30:30 +05:30
committed by GitHub
8 changed files with 281 additions and 150 deletions

View File

@@ -23,6 +23,7 @@ import {
ListItemText,
FormControlLabel,
Checkbox,
CircularProgress,
} from "@mui/material";
import {
Schedule,
@@ -154,6 +155,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 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 {
setIsLoading(false);
}
}, [setRecordings, notify, t]);
@@ -405,9 +409,7 @@ export const RecordingsTable = ({
}
useEffect(() => {
if (rows.length === 0) {
fetchRecordings();
}
fetchRecordings();
}, [fetchRecordings]);
useEffect(() => {
@@ -513,42 +515,81 @@ export const RecordingsTable = ({
</IconButton>
</Box>
</Box>
<TableContainer component={Paper} sx={{ width: '100%', overflow: 'hidden', marginTop: '15px' }}>
<Table stickyHeader aria-label="sticky table">
<TableHead>
<TableRow>
{columns.map((column) => (
<MemoizedTableCell
key={column.id}
style={{ minWidth: column.minWidth }}
>
{column.label}
</MemoizedTableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{visibleRows.map((row) => (
<TableRowMemoized
key={row.id}
row={row}
columns={columns}
handlers={handlers}
/>
))}
</TableBody>
</Table>
</TableContainer>
<TablePagination
rowsPerPageOptions={[10, 25, 50, 100]}
component="div"
count={filteredRows.length}
rowsPerPage={rowsPerPage}
page={page}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
/>
{isLoading ? (
<Box
display="flex"
justifyContent="center"
alignItems="center"
sx={{
minHeight: '60vh',
width: '100%'
}}
>
<CircularProgress size={60} />
</Box>
) : filteredRows.length === 0 ? (
<Box
display="flex"
flexDirection="column"
alignItems="center"
justifyContent="center"
sx={{
minHeight: 300,
textAlign: 'center',
color: 'text.secondary'
}}
>
<Typography variant="h6" gutterBottom>
{debouncedSearchTerm ? t('recordingtable.placeholder.search') : t('recordingtable.placeholder.title')}
</Typography>
<Typography variant="body2" color="text.secondary">
{debouncedSearchTerm
? t('recordingtable.search_criteria')
: t('recordingtable.placeholder.body')
}
</Typography>
</Box>
) : (
<>
<TableContainer component={Paper} sx={{ width: '100%', overflow: 'hidden', marginTop: '15px' }}>
<Table stickyHeader aria-label="sticky table">
<TableHead>
<TableRow>
{columns.map((column) => (
<MemoizedTableCell
key={column.id}
style={{ minWidth: column.minWidth }}
>
{column.label}
</MemoizedTableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{visibleRows.map((row) => (
<TableRowMemoized
key={row.id}
row={row}
columns={columns}
handlers={handlers}
/>
))}
</TableBody>
</Table>
</TableContainer>
<TablePagination
rowsPerPageOptions={[10, 25, 50, 100]}
component="div"
count={filteredRows.length}
rowsPerPage={rowsPerPage}
page={page}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
/>
</>
)}
<GenericModal isOpen={isWarningModalOpen} onClose={() => setWarningModalOpen(false)} modalStyle={modalStyle}>
<div style={{ padding: '10px' }}>
<Typography variant="h6" gutterBottom>{t('recordingtable.warning_modal.title')}</Typography>

View File

@@ -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,6 +134,7 @@ export const RunsTable: React.FC<RunsTableProps> = ({
const [rows, setRows] = useState<Data[]>([]);
const [searchTerm, setSearchTerm] = useState('');
const [isLoading, setIsLoading] = useState(true);
const [paginationStates, setPaginationStates] = useState<PaginationState>({});
@@ -224,6 +225,8 @@ export const RunsTable: React.FC<RunsTableProps> = ({
}
} catch (error) {
notify('error', t('runstable.notifications.fetch_error'));
} finally {
setIsLoading(false);
}
}, [notify, t]);
@@ -231,6 +234,7 @@ export const RunsTable: React.FC<RunsTableProps> = ({
let mounted = true;
if (rows.length === 0 || rerenderRuns) {
setIsLoading(true);
fetchRuns().then(() => {
if (mounted) {
setRerenderRuns(false);
@@ -378,102 +382,140 @@ export const RunsTable: React.FC<RunsTableProps> = ({
/>
</Box>
<TableContainer component={Paper} sx={{ width: '100%', overflow: 'hidden' }}>
{Object.entries(groupedRows)
.slice(
accordionPage * accordionsPerPage,
accordionPage * accordionsPerPage + accordionsPerPage
)
.map(([robotMetaId, data]) => (
<Accordion
key={robotMetaId}
onChange={(event, isExpanded) => handleAccordionChange(robotMetaId, isExpanded)}
TransitionProps={{ unmountOnExit: true }} // Optimize accordion rendering
>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography variant="h6">{data[0].name}</Typography>
</AccordionSummary>
<AccordionDetails>
<Table stickyHeader aria-label="sticky table">
<TableHead>
<TableRow>
<TableCell />
{translatedColumns.map((column) => (
<TableCell
key={column.id}
align={column.align}
style={{
minWidth: column.minWidth,
cursor: column.id === 'startedAt' || column.id === 'finishedAt' ? 'pointer' : 'default'
}}
onClick={() => {
if (column.id === 'startedAt' || column.id === 'finishedAt') {
handleSort(column.id, robotMetaId);
}
}}
>
<Tooltip
title={
(column.id === 'startedAt' || column.id === 'finishedAt')
? t('runstable.sort_tooltip')
: ''
}
>
<Box sx={{
display: 'flex',
alignItems: 'center',
gap: 1,
'&:hover': {
'& .sort-icon': {
opacity: 1
{isLoading? (
<Box
display="flex"
justifyContent="center"
alignItems="center"
sx={{
minHeight: '60vh',
width: '100%'
}}
>
<CircularProgress size={60} />
</Box>
) : Object.keys(groupedRows).length === 0 ? (
<Box
display="flex"
flexDirection="column"
alignItems="center"
justifyContent="center"
sx={{
minHeight: 300,
textAlign: 'center',
color: 'text.secondary'
}}
>
<Typography variant="h6" gutterBottom>
{searchTerm ? t('runstable.placeholder.search') : t('runstable.placeholder.title')}
</Typography>
<Typography variant="body2" color="text.secondary">
{searchTerm
? t('recordingtable.search_criteria')
: t('runstable.placeholder.body')
}
</Typography>
</Box>
) : (
<>
<TableContainer component={Paper} sx={{ width: '100%', overflow: 'hidden' }}>
{Object.entries(groupedRows)
.slice(
accordionPage * accordionsPerPage,
accordionPage * accordionsPerPage + accordionsPerPage
)
.map(([robotMetaId, data]) => (
<Accordion
key={robotMetaId}
onChange={(event, isExpanded) => handleAccordionChange(robotMetaId, isExpanded)}
TransitionProps={{ unmountOnExit: true }} // Optimize accordion rendering
>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography variant="h6">{data[0].name}</Typography>
</AccordionSummary>
<AccordionDetails>
<Table stickyHeader aria-label="sticky table">
<TableHead>
<TableRow>
<TableCell />
{translatedColumns.map((column) => (
<TableCell
key={column.id}
align={column.align}
style={{
minWidth: column.minWidth,
cursor: column.id === 'startedAt' || column.id === 'finishedAt' ? 'pointer' : 'default'
}}
onClick={() => {
if (column.id === 'startedAt' || column.id === 'finishedAt') {
handleSort(column.id, robotMetaId);
}
}
}}>
{column.label}
<Box className="sort-icon" sx={{
display: 'flex',
alignItems: 'center',
opacity: accordionSortConfigs[robotMetaId]?.field === column.id ? 1 : 0.3,
transition: 'opacity 0.2s'
}}>
{renderSortIcon(column, robotMetaId)}
</Box>
</Box>
</Tooltip>
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{renderTableRows(data, robotMetaId)}
</TableBody>
</Table>
}}
>
<Tooltip
title={
(column.id === 'startedAt' || column.id === 'finishedAt')
? t('runstable.sort_tooltip')
: ''
}
>
<Box sx={{
display: 'flex',
alignItems: 'center',
gap: 1,
'&:hover': {
'& .sort-icon': {
opacity: 1
}
}
}}>
{column.label}
<Box className="sort-icon" sx={{
display: 'flex',
alignItems: 'center',
opacity: accordionSortConfigs[robotMetaId]?.field === column.id ? 1 : 0.3,
transition: 'opacity 0.2s'
}}>
{renderSortIcon(column, robotMetaId)}
</Box>
</Box>
</Tooltip>
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{renderTableRows(data, robotMetaId)}
</TableBody>
</Table>
<TablePagination
component="div"
count={data.length}
rowsPerPage={getPaginationState(robotMetaId).rowsPerPage}
page={getPaginationState(robotMetaId).page}
onPageChange={(_, newPage) => handleChangePage(robotMetaId, newPage)}
onRowsPerPageChange={(event) =>
handleChangeRowsPerPage(robotMetaId, +event.target.value)
}
rowsPerPageOptions={[10, 25, 50, 100]}
/>
</AccordionDetails>
</Accordion>
))}
</TableContainer>
<TablePagination
component="div"
count={data.length}
rowsPerPage={getPaginationState(robotMetaId).rowsPerPage}
page={getPaginationState(robotMetaId).page}
onPageChange={(_, newPage) => handleChangePage(robotMetaId, newPage)}
onRowsPerPageChange={(event) =>
handleChangeRowsPerPage(robotMetaId, +event.target.value)
}
rowsPerPageOptions={[10, 25, 50, 100]}
/>
</AccordionDetails>
</Accordion>
))}
</TableContainer>
<TablePagination
component="div"
count={Object.keys(groupedRows).length}
page={accordionPage}
rowsPerPage={accordionsPerPage}
onPageChange={handleAccordionPageChange}
onRowsPerPageChange={handleAccordionsPerPageChange}
rowsPerPageOptions={[10, 25, 50, 100]}
/>
<TablePagination
component="div"
count={Object.keys(groupedRows).length}
page={accordionPage}
rowsPerPage={accordionsPerPage}
onPageChange={handleAccordionPageChange}
onRowsPerPageChange={handleAccordionsPerPageChange}
rowsPerPageOptions={[10, 25, 50, 100]}
/>
</>
)}
</React.Fragment>
);
};