Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
99 changes: 63 additions & 36 deletions src/components/robot/RecordingsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -513,42 +513,69 @@ 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}
/>

{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>
Expand Down
216 changes: 121 additions & 95 deletions src/components/run/RunsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -378,102 +378,128 @@ 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
{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')
Comment on lines +413 to +415
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix wrong i18n namespace for search criteria.

This references recordingtable.* inside RunsTable; should use runstable.*.

-              ? t('recordingtable.search_criteria')
+              ? t('runstable.search_criteria')
📝 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
{searchTerm
? t('recordingtable.search_criteria')
: t('runstable.placeholder.body')
{searchTerm
? t('runstable.search_criteria')
: t('runstable.placeholder.body')}
🤖 Prompt for AI Agents
In src/components/run/RunsTable.tsx around lines 397 to 399, the i18n key used
for the search criteria message is incorrectly namespaced as
recordingtable.search_criteria; change it to use the runstable namespace
(runstable.search_criteria) so it matches this component's translation keys and
returns the correct localized string.

}
</Typography>
</Box>
) : (
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify i18n keys exist for new runstable strings.

Ensure these keys exist: runstable.placeholder.title/body/search, runstable.search_criteria, runstable.sort_tooltip.

Also applies to: 424-468, 492-500


🏁 Script executed:

#!/bin/bash
# Check runstable translation keys
rg -n --glob '!**/node_modules/**' \
  -e '"runstable\.placeholder\.title"' \
  -e '"runstable\.placeholder\.body"' \
  -e '"runstable\.placeholder\.search"' \
  -e '"runstable\.search_criteria"' \
  -e '"runstable\.sort_tooltip"' \
  -- **/*.{json,ts,tsx}

Length of output: 368


🏁 Script executed:

#!/bin/bash
# List JSON translation files in src
find src -type f -name '*.json' ! -path '*/node_modules/*'
# Show first 200 lines of src/i18n.ts to locate translation resource loading
echo "----- src/i18n.ts -----"
head -n 200 src/i18n.ts

Length of output: 695


🏁 Script executed:

#!/bin/bash
# List all JSON files in the repo (excluding node_modules)
find . -type f -name '*.json' ! -path '*/node_modules/*'
# Find any 'locales' directories in the repo
find . -type d -name 'locales' ! -path '*/node_modules/*'
# If a public/locales directory exists, list its contents
if [ -d "./public/locales" ]; then
  echo "---- public/locales ----"
  ls -1 ./public/locales
fi

Length of output: 639


🏁 Script executed:

#!/bin/bash
# Verify translation keys in locale JSON files
python3 - << 'EOF'
import json, os
languages = ['en','es','ja','zh','de','tr']
keys = [
    'runstable.placeholder.title',
    'runstable.placeholder.body',
    'runstable.placeholder.search',
    'runstable.search_criteria',
    'runstable.sort_tooltip'
]
for lang in languages:
    path = os.path.join('public','locales', f'{lang}.json')
    try:
        data = json.load(open(path, 'r', encoding='utf-8'))
    except Exception as e:
        print(f"{lang}.json: ERROR loading file ({e})")
        continue
    def flatten(d, prefix='', out=None):
        out = {} if out is None else out
        if isinstance(d, dict):
            for k,v in d.items():
                new_prefix = f"{prefix}.{k}" if prefix else k
                flatten(v, new_prefix, out)
        else:
            out[prefix] = d
        return out
    flat = flatten(data)
    missing = [key for key in keys if key not in flat]
    status = "None" if not missing else ", ".join(missing)
    print(f"{lang}.json missing: {status}")
EOF

Length of output: 791


Add missing i18n keys for runstable placeholders and search criteria
Missing in all locale JSONs:

  • runstable.placeholder.title
  • runstable.placeholder.body
  • runstable.placeholder.search
  • runstable.search_criteria

(The runstable.sort_tooltip key is already present.)

🤖 Prompt for AI Agents
In src/components/run/RunsTable.tsx lines 381-403, the i18n keys used for the
empty-state and search copy are missing from the locale JSONs; add the following
keys to every locale file: "runstable.placeholder.title",
"runstable.placeholder.body", "runstable.placeholder.search", and
"runstable.search_criteria" (ensure the translations are supplied for each
locale). Also verify the component uses the intended key (it references
recordingtable.search_criteria in one place) and either add that key to locales
or change the component to use runstable.search_criteria so keys and code match.

<>
<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')
: ''
}
}
}}>
{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={Object.keys(groupedRows).length}
page={accordionPage}
rowsPerPage={accordionsPerPage}
onPageChange={handleAccordionPageChange}
onRowsPerPageChange={handleAccordionsPerPageChange}
rowsPerPageOptions={[10, 25, 50, 100]}
/>
>
<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={Object.keys(groupedRows).length}
page={accordionPage}
rowsPerPage={accordionsPerPage}
onPageChange={handleAccordionPageChange}
onRowsPerPageChange={handleAccordionsPerPageChange}
rowsPerPageOptions={[10, 25, 50, 100]}
/>
</>
)}
</React.Fragment>
);
};