Merge pull request #314 from getmaxun/reset-browser

feat: reset remote browser recording state
This commit is contained in:
Karishma Shukla
2025-01-13 13:57:47 +05:30
committed by GitHub
10 changed files with 203 additions and 26 deletions

View File

@@ -1,18 +1,44 @@
import React, { useState } from 'react'
import { Grid, Button, Box, Typography } from '@mui/material';
import { Grid, Button, Box, Typography, IconButton, Menu, MenuItem, ListItemText } from '@mui/material';
import { SaveRecording } from "../recorder/SaveRecording";
import { useGlobalInfoStore } from '../../context/globalInfo';
import { useActionContext } from '../../context/browserActions';
import { useBrowserSteps } from '../../context/browserSteps';
import { stopRecording } from "../../api/recording";
import { useNavigate } from 'react-router-dom';
import { GenericModal } from "../ui/GenericModal";
import { useTranslation } from 'react-i18next';
import { emptyWorkflow } from '../../shared/constants';
import { useSocketStore } from '../../context/socket';
import { MoreHoriz } from '@mui/icons-material';
const BrowserRecordingSave = () => {
const { t } = useTranslation();
const [openModal, setOpenModal] = useState<boolean>(false);
const { recordingName, browserId, setBrowserId, notify } = useGlobalInfoStore();
const [openDiscardModal, setOpenDiscardModal] = useState<boolean>(false);
const [openResetModal, setOpenResetModal] = useState<boolean>(false);
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const { recordingName, browserId, initialUrl, setRecordingUrl, setBrowserId, notify, setCurrentWorkflowActionsState, resetInterpretationLog } = useGlobalInfoStore();
const navigate = useNavigate();
const { socket } = useSocketStore();
const {
stopGetText,
stopGetList,
stopGetScreenshot,
stopPaginationMode,
stopLimitMode,
setCaptureStage,
updatePaginationType,
updateLimitType,
updateCustomLimit,
setShowLimitOptions,
setShowPaginationOptions,
setWorkflow,
} = useActionContext();
const { browserSteps, deleteBrowserStep } = useBrowserSteps();
const goToMainMenu = async () => {
if (browserId) {
await stopRecording(browserId);
@@ -22,6 +48,58 @@ const BrowserRecordingSave = () => {
navigate('/');
};
const performReset = () => {
stopGetText();
stopGetList();
stopGetScreenshot();
stopPaginationMode();
stopLimitMode();
setShowLimitOptions(false);
setShowPaginationOptions(false);
setCaptureStage('initial');
updatePaginationType('');
updateLimitType('');
updateCustomLimit('');
setCurrentWorkflowActionsState({
hasScrapeListAction: false,
hasScreenshotAction: false,
hasScrapeSchemaAction: false
});
setWorkflow(emptyWorkflow);
resetInterpretationLog();
// Clear all browser steps
browserSteps.forEach(step => {
deleteBrowserStep(step.id);
});
if (socket) {
socket?.emit('new-recording');
socket.emit('input:url', initialUrl);
// Update the URL in the navbar to match
setRecordingUrl(initialUrl);
}
// Close the reset confirmation modal
setOpenResetModal(false);
// Notify user
notify('info', t('browser_recording.notifications.environment_reset'));
};
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
return (
<Grid container>
<Grid item xs={12} md={3} lg={3}>
@@ -40,7 +118,7 @@ const BrowserRecordingSave = () => {
height: "48px"
}}>
<Button
onClick={() => setOpenModal(true)}
onClick={() => setOpenDiscardModal(true)}
variant="outlined"
color="error"
sx={{
@@ -53,15 +131,62 @@ const BrowserRecordingSave = () => {
>
{t('right_panel.buttons.discard')}
</Button>
<GenericModal isOpen={openModal} onClose={() => setOpenModal(false)} modalStyle={modalStyle}>
{/* Reset Button */}
<IconButton
aria-label="options"
size="small"
onClick={handleClick}
style={{
color: 'whitesmoke',
}}
>
<MoreHoriz />
</IconButton>
<Menu
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={handleClose}
>
<MenuItem onClick={() => { setOpenResetModal(true); handleClose(); }}>
<ListItemText>{t('right_panel.buttons.reset')}</ListItemText>
</MenuItem>
</Menu>
<SaveRecording fileName={recordingName} />
{/* Discard Confirmation Modal */}
<GenericModal isOpen={openDiscardModal} onClose={() => setOpenDiscardModal(false)} modalStyle={modalStyle}>
<Box p={2}>
<Typography variant="h6">{t('browser_recording.modal.confirm_discard')}</Typography>
<Box display="flex" justifyContent="space-between" mt={2}>
<Button onClick={goToMainMenu} variant="contained" color="error">
{t('right_panel.buttons.discard')}
</Button>
<Button onClick={() => setOpenDiscardModal(false)} variant="outlined">
{t('right_panel.buttons.cancel')}
</Button>
</Box>
</Box>
</GenericModal>
{/* Reset Confirmation Modal */}
<GenericModal isOpen={openResetModal} onClose={() => setOpenResetModal(false)} modalStyle={modalStyle}>
<Box p={2}>
<Typography variant="h6">{t('browser_recording.modal.confirm_reset')}</Typography>
<Typography variant="body2" sx={{ mt: 1, mb: 2 }}>
{t('browser_recording.modal.reset_warning')}
</Typography>
<Box display="flex" justifyContent="space-between" mt={2}>
<Button
onClick={performReset}
variant="contained"
color="primary"
>
{t('right_panel.buttons.confirm_reset')}
</Button>
<Button
onClick={() => setOpenModal(false)}
onClick={() => setOpenResetModal(false)}
variant="outlined"
sx={{
color: '#ff00c3 !important',
@@ -73,12 +198,11 @@ const BrowserRecordingSave = () => {
</Box>
</Box>
</GenericModal>
<SaveRecording fileName={recordingName} />
</div>
</Grid>
</Grid>
);
}
};
export default BrowserRecordingSave;

View File

@@ -45,13 +45,12 @@ interface RightSidePanelProps {
}
export const RightSidePanel: React.FC<RightSidePanelProps> = ({ onFinishCapture }) => {
const [workflow, setWorkflow] = useState<WorkflowFile>(emptyWorkflow);
const [textLabels, setTextLabels] = useState<{ [id: string]: string }>({});
const [errors, setErrors] = useState<{ [id: string]: string }>({});
const [confirmedTextSteps, setConfirmedTextSteps] = useState<{ [id: string]: boolean }>({});
const [confirmedListTextFields, setConfirmedListTextFields] = useState<{ [listId: string]: { [fieldKey: string]: boolean } }>({});
const [showPaginationOptions, setShowPaginationOptions] = useState(false);
const [showLimitOptions, setShowLimitOptions] = useState(false);
// const [showPaginationOptions, setShowPaginationOptions] = useState(false);
// const [showLimitOptions, setShowLimitOptions] = useState(false);
const [showCaptureList, setShowCaptureList] = useState(true);
const [showCaptureScreenshot, setShowCaptureScreenshot] = useState(true);
const [showCaptureText, setShowCaptureText] = useState(true);
@@ -61,7 +60,7 @@ export const RightSidePanel: React.FC<RightSidePanelProps> = ({ onFinishCapture
const [isCaptureListConfirmed, setIsCaptureListConfirmed] = useState(false);
const { lastAction, notify, currentWorkflowActionsState, setCurrentWorkflowActionsState, resetInterpretationLog } = useGlobalInfoStore();
const { getText, startGetText, stopGetText, getScreenshot, startGetScreenshot, stopGetScreenshot, getList, startGetList, stopGetList, startPaginationMode, stopPaginationMode, paginationType, updatePaginationType, limitType, customLimit, updateLimitType, updateCustomLimit, stopLimitMode, startLimitMode, captureStage, setCaptureStage } = useActionContext();
const { getText, startGetText, stopGetText, getScreenshot, startGetScreenshot, stopGetScreenshot, getList, startGetList, stopGetList, startPaginationMode, stopPaginationMode, paginationType, updatePaginationType, limitType, customLimit, updateLimitType, updateCustomLimit, stopLimitMode, startLimitMode, captureStage, setCaptureStage, showPaginationOptions, setShowPaginationOptions, showLimitOptions, setShowLimitOptions, workflow, setWorkflow } = useActionContext();
const { browserSteps, updateBrowserTextStepLabel, deleteBrowserStep, addScreenshotStep, updateListTextFieldLabel, removeListTextField } = useBrowserSteps();
const { id, socket } = useSocketStore();
const { t } = useTranslation();
@@ -69,7 +68,7 @@ export const RightSidePanel: React.FC<RightSidePanelProps> = ({ onFinishCapture
const workflowHandler = useCallback((data: WorkflowFile) => {
setWorkflow(data);
//setRecordingLength(data.workflow.length);
}, [workflow])
}, [])
useEffect(() => {
if (socket) {

View File

@@ -85,7 +85,7 @@ export const RecordingsTable = ({ handleEditRecording, handleRunRecording, handl
},
];
const { notify, setRecordings, browserId, setBrowserId, recordingUrl, setRecordingUrl, recordingName, setRecordingName, recordingId, setRecordingId } = useGlobalInfoStore();
const { notify, setRecordings, browserId, setBrowserId, setInitialUrl, recordingUrl, setRecordingUrl, recordingName, setRecordingName, recordingId, setRecordingId } = useGlobalInfoStore();
const navigate = useNavigate();
const handleChangePage = (event: unknown, newPage: number) => {
@@ -142,6 +142,11 @@ export const RecordingsTable = ({ handleEditRecording, handleRunRecording, handl
handleStartRecording();
};
const setBrowserRecordingUrl = (event: React.ChangeEvent<HTMLInputElement>) => {
setInitialUrl(event.target.value);
setRecordingUrl(event.target.value);
}
useEffect(() => {
if (rows.length === 0) {
fetchRecordings();
@@ -307,7 +312,7 @@ export const RecordingsTable = ({ handleEditRecording, handleRunRecording, handl
variant="outlined"
fullWidth
value={recordingUrl}
onChange={(e: any) => setRecordingUrl(e.target.value)}
onChange={setBrowserRecordingUrl}
style={{ marginBottom: '20px', marginTop: '20px' }}
/>
<Button