Merge pull request #856 from saniyafatima07/develop
Feat: Implemented delete confirmation modal
This commit is contained in:
@@ -42,6 +42,7 @@ import { Add } from "@mui/icons-material";
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { canCreateBrowserInState, getActiveBrowserId, stopRecording } from "../../api/recording";
|
import { canCreateBrowserInState, getActiveBrowserId, stopRecording } from "../../api/recording";
|
||||||
import { GenericModal } from '../ui/GenericModal';
|
import { GenericModal } from '../ui/GenericModal';
|
||||||
|
import { useTheme } from '@mui/material/styles';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
@@ -148,12 +149,15 @@ export const RecordingsTable = ({
|
|||||||
handleEditRobot,
|
handleEditRobot,
|
||||||
handleDuplicateRobot }: RecordingsTableProps) => {
|
handleDuplicateRobot }: RecordingsTableProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const theme = useTheme();
|
||||||
const [page, setPage] = React.useState(0);
|
const [page, setPage] = React.useState(0);
|
||||||
const [rowsPerPage, setRowsPerPage] = React.useState(10);
|
const [rowsPerPage, setRowsPerPage] = React.useState(10);
|
||||||
const { data: recordingsData = [], isLoading: isFetching, error, refetch } = useCachedRecordings();
|
const { data: recordingsData = [], isLoading: isFetching, error, refetch } = useCachedRecordings();
|
||||||
const [isModalOpen, setModalOpen] = React.useState(false);
|
const [isModalOpen, setModalOpen] = React.useState(false);
|
||||||
const [searchTerm, setSearchTerm] = React.useState('');
|
const [searchTerm, setSearchTerm] = React.useState('');
|
||||||
const [isWarningModalOpen, setWarningModalOpen] = React.useState(false);
|
const [isWarningModalOpen, setWarningModalOpen] = React.useState(false);
|
||||||
|
const [isDeleteConfirmOpen, setDeleteConfirmOpen] = React.useState(false);
|
||||||
|
const [pendingDeleteId, setPendingDeleteId] = React.useState<string | null>(null);
|
||||||
const [activeBrowserId, setActiveBrowserId] = React.useState('');
|
const [activeBrowserId, setActiveBrowserId] = React.useState('');
|
||||||
|
|
||||||
const columns = useMemo(() => [
|
const columns = useMemo(() => [
|
||||||
@@ -431,6 +435,32 @@ export const RecordingsTable = ({
|
|||||||
return filteredRows.slice(start, start + rowsPerPage);
|
return filteredRows.slice(start, start + rowsPerPage);
|
||||||
}, [filteredRows, page, rowsPerPage]);
|
}, [filteredRows, page, rowsPerPage]);
|
||||||
|
|
||||||
|
const openDeleteConfirm = React.useCallback((id: string) => {
|
||||||
|
setPendingDeleteId(String(id));
|
||||||
|
setDeleteConfirmOpen(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const confirmDeleteRecording = React.useCallback(async () => {
|
||||||
|
if (!pendingDeleteId) return;
|
||||||
|
const hasRuns = await checkRunsForRecording(pendingDeleteId);
|
||||||
|
if (hasRuns) {
|
||||||
|
notify('warning', t('recordingtable.notifications.delete_warning'));
|
||||||
|
setDeleteConfirmOpen(false);
|
||||||
|
setPendingDeleteId(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const success = await deleteRecordingFromStorage(pendingDeleteId);
|
||||||
|
if (success) {
|
||||||
|
notify('success', t('recordingtable.notifications.delete_success'));
|
||||||
|
refetch();
|
||||||
|
}
|
||||||
|
setDeleteConfirmOpen(false);
|
||||||
|
setPendingDeleteId(null);
|
||||||
|
}, [pendingDeleteId, notify, t, refetch]);
|
||||||
|
|
||||||
|
const pendingRow = pendingDeleteId ? rows.find(r => String(r.id) === pendingDeleteId) : null;
|
||||||
|
|
||||||
const handlers = useMemo(() => ({
|
const handlers = useMemo(() => ({
|
||||||
handleRunRecording,
|
handleRunRecording,
|
||||||
handleScheduleRecording,
|
handleScheduleRecording,
|
||||||
@@ -439,19 +469,7 @@ export const RecordingsTable = ({
|
|||||||
handleEditRobot,
|
handleEditRobot,
|
||||||
handleDuplicateRobot,
|
handleDuplicateRobot,
|
||||||
handleRetrainRobot,
|
handleRetrainRobot,
|
||||||
handleDelete: async (id: string) => {
|
handleDelete: async (id: string) => openDeleteConfirm(id)
|
||||||
const hasRuns = await checkRunsForRecording(id);
|
|
||||||
if (hasRuns) {
|
|
||||||
notify('warning', t('recordingtable.notifications.delete_warning'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const success = await deleteRecordingFromStorage(id);
|
|
||||||
if (success) {
|
|
||||||
notify('success', t('recordingtable.notifications.delete_success'));
|
|
||||||
refetch();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}), [handleRunRecording, handleScheduleRecording, handleIntegrateRecording, handleSettingsRecording, handleEditRobot, handleDuplicateRobot, handleRetrainRobot, notify, t, refetch]);
|
}), [handleRunRecording, handleScheduleRecording, handleIntegrateRecording, handleSettingsRecording, handleEditRobot, handleDuplicateRobot, handleRetrainRobot, notify, t, refetch]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -628,6 +646,33 @@ export const RecordingsTable = ({
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</GenericModal>
|
</GenericModal>
|
||||||
|
<GenericModal
|
||||||
|
isOpen={isDeleteConfirmOpen}
|
||||||
|
onClose={() => { setDeleteConfirmOpen(false); setPendingDeleteId(null); }}
|
||||||
|
modalStyle={{ ...modalStyle, padding: 0, backgroundColor: 'transparent', width: 'auto', maxWidth: '520px' }}
|
||||||
|
>
|
||||||
|
|
||||||
|
<Box sx={{ padding: theme.spacing(3), borderRadius: 2, backgroundColor: theme.palette.mode === 'dark' ? theme.palette.grey[900] : theme.palette.background.paper, color: theme.palette.text.primary, width: { xs: '90vw', sm: '460px', md: '420px' }, maxWidth: '90vw', boxSizing: 'border-box', mx: 'auto' }}>
|
||||||
|
<Typography variant="h6" sx={{ fontWeight: 600 }}>
|
||||||
|
{t('recordingtable.delete_confirm.title', { name: pendingRow?.name, defaultValue: 'Delete {{name}}?' })}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body1" sx={{ mb: 2 }}>
|
||||||
|
{t('recordingtable.delete_confirm.message', {
|
||||||
|
name: pendingRow?.name,
|
||||||
|
defaultValue: 'Are you sure you want to delete the robot "{{name}}"?'
|
||||||
|
})}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Box display="flex" justifyContent="flex-end" mt={2} gap={1}>
|
||||||
|
<Button onClick={() => { setDeleteConfirmOpen(false); setPendingDeleteId(null); }} variant="outlined">
|
||||||
|
{t('common.cancel', { defaultValue: 'Cancel' })}
|
||||||
|
</Button>
|
||||||
|
<Button onClick={confirmDeleteRecording} variant="contained" color="primary">
|
||||||
|
{t('common.delete', { defaultValue: 'Delete' })}
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</GenericModal>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import * as React from "react";
|
|||||||
import TableRow from "@mui/material/TableRow";
|
import TableRow from "@mui/material/TableRow";
|
||||||
import TableCell from "@mui/material/TableCell";
|
import TableCell from "@mui/material/TableCell";
|
||||||
import { Box, Collapse, IconButton, Typography, Chip, TextField } from "@mui/material";
|
import { Box, Collapse, IconButton, Typography, Chip, TextField } from "@mui/material";
|
||||||
|
import { Button } from "@mui/material";
|
||||||
import { DeleteForever, KeyboardArrowDown, KeyboardArrowUp, Settings } from "@mui/icons-material";
|
import { DeleteForever, KeyboardArrowDown, KeyboardArrowUp, Settings } from "@mui/icons-material";
|
||||||
import { deleteRunFromStorage } from "../../api/storage";
|
import { deleteRunFromStorage } from "../../api/storage";
|
||||||
import { columns, Data } from "./RunsTable";
|
import { columns, Data } from "./RunsTable";
|
||||||
@@ -11,6 +12,7 @@ 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 { useTheme } from "@mui/material/styles";
|
||||||
|
|
||||||
interface RunTypeChipProps {
|
interface RunTypeChipProps {
|
||||||
runByUserId?: string;
|
runByUserId?: string;
|
||||||
@@ -39,6 +41,8 @@ interface CollapsibleRowProps {
|
|||||||
}
|
}
|
||||||
export const CollapsibleRow = ({ row, handleDelete, isOpen, onToggleExpanded, currentLog, abortRunHandler, runningRecordingName, urlRunId }: CollapsibleRowProps) => {
|
export const CollapsibleRow = ({ row, handleDelete, isOpen, onToggleExpanded, currentLog, abortRunHandler, runningRecordingName, urlRunId }: CollapsibleRowProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const theme = useTheme();
|
||||||
|
const [isDeleteOpen, setDeleteOpen] = useState(false);
|
||||||
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.runByScheduleId
|
const runByLabel = row.runByScheduleId
|
||||||
@@ -83,6 +87,17 @@ export const CollapsibleRow = ({ row, handleDelete, isOpen, onToggleExpanded, cu
|
|||||||
fetchUserEmail();
|
fetchUserEmail();
|
||||||
}, [row.runByUserId]);
|
}, [row.runByUserId]);
|
||||||
|
|
||||||
|
const handleConfirmDelete = async () => {
|
||||||
|
try {
|
||||||
|
const res = await deleteRunFromStorage(`${row.runId}`);
|
||||||
|
if (res) {
|
||||||
|
handleDelete();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setDeleteOpen(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }} hover role="checkbox" tabIndex={-1} key={row.id}>
|
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }} hover role="checkbox" tabIndex={-1} key={row.id}>
|
||||||
@@ -120,13 +135,7 @@ export const CollapsibleRow = ({ row, handleDelete, isOpen, onToggleExpanded, cu
|
|||||||
case 'delete':
|
case 'delete':
|
||||||
return (
|
return (
|
||||||
<TableCell key={column.id} align={column.align}>
|
<TableCell key={column.id} align={column.align}>
|
||||||
<IconButton aria-label="add" size="small" onClick={() => {
|
<IconButton aria-label="delete" size="small" onClick={() => setDeleteOpen(true)}>
|
||||||
deleteRunFromStorage(`${row.runId}`).then((result: boolean) => {
|
|
||||||
if (result) {
|
|
||||||
handleDelete();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}}>
|
|
||||||
<DeleteForever />
|
<DeleteForever />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
@@ -192,6 +201,32 @@ export const CollapsibleRow = ({ row, handleDelete, isOpen, onToggleExpanded, cu
|
|||||||
</Collapse>
|
</Collapse>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
||||||
|
<GenericModal isOpen={isDeleteOpen} onClose={() => setDeleteOpen(false)} modalStyle={{ ...modalStyle, padding: 0, backgroundColor: 'transparent', width: 'auto', maxWidth: '520px' }}>
|
||||||
|
<Box sx={{ padding: theme.spacing(3), borderRadius: 2, backgroundColor: theme.palette.mode === 'dark' ? theme.palette.grey[900] : theme.palette.background.paper, color: theme.palette.text.primary, width: { xs: '90vw', sm: '460px', md: '420px' }, maxWidth: '90vw', boxSizing: 'border-box', mx: 'auto' }}>
|
||||||
|
<Typography variant="h6" sx={{ fontWeight: 600 }}>
|
||||||
|
{t('runs_table.delete_confirm.title', {
|
||||||
|
name: row.name,
|
||||||
|
defaultValue: 'Delete run "{{name}}"?'
|
||||||
|
})}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body1" sx={{ mb: 2 }}>
|
||||||
|
{t('runs_table.delete_confirm.message', {
|
||||||
|
name: row.name,
|
||||||
|
defaultValue: 'Are you sure you want to delete the run "{{name}}"?'
|
||||||
|
})}
|
||||||
|
</Typography>
|
||||||
|
<Box display="flex" justifyContent="flex-end" gap={1}>
|
||||||
|
<Button onClick={() => setDeleteOpen(false)} variant="outlined">
|
||||||
|
{t('common.cancel', { defaultValue: 'Cancel' })}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={handleConfirmDelete} variant="contained" color="primary" sx={{ '&:hover': { backgroundColor: theme.palette.primary.dark } }}>
|
||||||
|
{t('common.delete', { defaultValue: 'Delete' })}
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</GenericModal>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user