diff --git a/src/components/browser/BrowserWindow.tsx b/src/components/browser/BrowserWindow.tsx index 83750f387..8ce6e0f95 100644 --- a/src/components/browser/BrowserWindow.tsx +++ b/src/components/browser/BrowserWindow.tsx @@ -85,7 +85,7 @@ export const BrowserWindow = () => { const [paginationSelector, setPaginationSelector] = useState(''); const { socket } = useSocketStore(); - const { notify } = useGlobalInfoStore(); + const { notify, currentTextActionId, currentListActionId } = useGlobalInfoStore(); const { getText, getList, paginationMode, paginationType, limitMode, captureStage } = useActionContext(); const { addTextStep, addListStep, updateListStepData } = useBrowserSteps(); @@ -326,8 +326,8 @@ export const BrowserWindow = () => { selector: highlighterData.selector, tag: highlighterData.elementInfo?.tagName, shadow: highlighterData.elementInfo?.isShadowRoot, - attribute - }); + attribute, + }, currentTextActionId || `text-${crypto.randomUUID()}`); } else { // Show the modal if there are multiple options setAttributeOptions(options); @@ -344,7 +344,7 @@ export const BrowserWindow = () => { if (paginationType !== '' && paginationType !== 'scrollDown' && paginationType !== 'scrollUp' && paginationType !== 'none') { setPaginationSelector(highlighterData.selector); notify(`info`, t('browser_window.attribute_modal.notifications.pagination_select_success')); - addListStep(listSelector!, fields, currentListId || 0, { type: paginationType, selector: highlighterData.selector }); + addListStep(listSelector!, fields, currentListId || 0, currentListActionId || `list-${crypto.randomUUID()}`, { type: paginationType, selector: highlighterData.selector }); socket?.emit('setPaginationMode', { pagination: false }); } return; @@ -412,6 +412,7 @@ export const BrowserWindow = () => { listSelector, updatedFields, currentListId, + currentListActionId || `list-${crypto.randomUUID()}`, { type: '', selector: paginationSelector } ); } @@ -449,7 +450,7 @@ export const BrowserWindow = () => { tag: selectedElement.info?.tagName, shadow: selectedElement.info?.isShadowRoot, attribute: attribute - }); + }, currentTextActionId || `text-${crypto.randomUUID()}`); } if (getList === true && listSelector && currentListId) { const newField: TextStep = { @@ -484,6 +485,7 @@ export const BrowserWindow = () => { listSelector, updatedFields, currentListId, + currentListActionId || `list-${crypto.randomUUID()}`, { type: '', selector: paginationSelector } ); } diff --git a/src/components/recorder/RightSidePanel.tsx b/src/components/recorder/RightSidePanel.tsx index 3383c9411..8d8cac22c 100644 --- a/src/components/recorder/RightSidePanel.tsx +++ b/src/components/recorder/RightSidePanel.tsx @@ -52,7 +52,7 @@ export const RightSidePanel: React.FC = ({ onFinishCapture const [isCaptureListConfirmed, setIsCaptureListConfirmed] = useState(false); const { panelHeight } = useBrowserDimensionsStore(); - const { lastAction, notify, currentWorkflowActionsState, setCurrentWorkflowActionsState, resetInterpretationLog } = useGlobalInfoStore(); + const { lastAction, notify, currentWorkflowActionsState, setCurrentWorkflowActionsState, resetInterpretationLog, currentListActionId, setCurrentListActionId, currentTextActionId, setCurrentTextActionId, currentScreenshotActionId, setCurrentScreenshotActionId } = useGlobalInfoStore(); const { getText, startGetText, stopGetText, getList, startGetList, stopGetList, @@ -69,7 +69,7 @@ export const RightSidePanel: React.FC = ({ onFinishCapture startAction, finishAction } = useActionContext(); - const { browserSteps, updateBrowserTextStepLabel, deleteBrowserStep, addScreenshotStep, updateListTextFieldLabel, removeListTextField, updateListStepLimit } = useBrowserSteps(); + const { browserSteps, updateBrowserTextStepLabel, deleteBrowserStep, addScreenshotStep, updateListTextFieldLabel, removeListTextField, updateListStepLimit, deleteStepsByActionId } = useBrowserSteps(); const { id, socket } = useSocketStore(); const { t } = useTranslation(); @@ -139,15 +139,21 @@ export const RightSidePanel: React.FC = ({ onFinishCapture const handleStartGetText = () => { setIsCaptureTextConfirmed(false); + const newActionId = `text-${crypto.randomUUID()}`; + setCurrentTextActionId(newActionId); startGetText(); } const handleStartGetList = () => { setIsCaptureListConfirmed(false); + const newActionId = `list-${crypto.randomUUID()}`; + setCurrentListActionId(newActionId); startGetList(); } const handleStartGetScreenshot = () => { + const newActionId = `screenshot-${crypto.randomUUID()}`; + setCurrentScreenshotActionId(newActionId); startGetScreenshot(); }; @@ -277,6 +283,7 @@ export const RightSidePanel: React.FC = ({ onFinishCapture socket?.emit('action', { action: 'scrapeSchema', settings }); } setIsCaptureTextConfirmed(true); + setCurrentTextActionId(''); resetInterpretationLog(); finishAction('text'); onFinishCapture(); @@ -337,6 +344,7 @@ export const RightSidePanel: React.FC = ({ onFinishCapture notify('error', t('right_panel.errors.unable_create_settings')); } handleStopGetList(); + setCurrentListActionId(''); resetInterpretationLog(); finishAction('list'); onFinishCapture(); @@ -434,35 +442,73 @@ export const RightSidePanel: React.FC = ({ onFinishCapture const discardGetText = useCallback(() => { stopGetText(); - browserSteps.forEach(step => { - if (step.type === 'text') { - deleteBrowserStep(step.id); - } - }); - setTextLabels({}); - setErrors({}); - setConfirmedTextSteps({}); + + if (currentTextActionId) { + const stepsToDelete = browserSteps + .filter(step => step.type === 'text' && step.actionId === currentTextActionId) + .map(step => step.id); + + deleteStepsByActionId(currentTextActionId); + + setTextLabels(prevLabels => { + const newLabels = { ...prevLabels }; + stepsToDelete.forEach(id => { + delete newLabels[id]; + }); + return newLabels; + }); + + setErrors(prevErrors => { + const newErrors = { ...prevErrors }; + stepsToDelete.forEach(id => { + delete newErrors[id]; + }); + return newErrors; + }); + + setConfirmedTextSteps(prev => { + const newConfirmed = { ...prev }; + stepsToDelete.forEach(id => { + delete newConfirmed[id]; + }); + return newConfirmed; + }); + } + + setCurrentTextActionId(''); setIsCaptureTextConfirmed(false); notify('error', t('right_panel.errors.capture_text_discarded')); - }, [browserSteps, stopGetText, deleteBrowserStep, notify, t]); + }, [currentTextActionId, browserSteps, stopGetText, deleteStepsByActionId, notify, t]); const discardGetList = useCallback(() => { stopGetList(); - browserSteps.forEach(step => { - if (step.type === 'list') { - deleteBrowserStep(step.id); - } - }); + + if (currentListActionId) { + const listStepsToDelete = browserSteps + .filter(step => step.type === 'list' && step.actionId === currentListActionId) + .map(step => step.id); + + deleteStepsByActionId(currentListActionId); + + setConfirmedListTextFields(prev => { + const newConfirmed = { ...prev }; + listStepsToDelete.forEach(id => { + delete newConfirmed[id]; + }); + return newConfirmed; + }); + } + resetListState(); stopPaginationMode(); stopLimitMode(); setShowPaginationOptions(false); setShowLimitOptions(false); setCaptureStage('initial'); - setConfirmedListTextFields({}); + setCurrentListActionId(''); setIsCaptureListConfirmed(false); notify('error', t('right_panel.errors.capture_list_discarded')); - }, [browserSteps, stopGetList, deleteBrowserStep, resetListState, setShowPaginationOptions, setShowLimitOptions, setCaptureStage, notify, t]); + }, [currentListActionId, browserSteps, stopGetList, deleteStepsByActionId, resetListState, setShowPaginationOptions, setShowLimitOptions, setCaptureStage, notify, t]); const captureScreenshot = (fullPage: boolean) => { const screenshotSettings: ScreenshotSettings = { @@ -474,7 +520,7 @@ export const RightSidePanel: React.FC = ({ onFinishCapture scale: 'device', }; socket?.emit('action', { action: 'screenshot', settings: screenshotSettings }); - addScreenshotStep(fullPage); + addScreenshotStep(fullPage, currentScreenshotActionId); stopGetScreenshot(); resetInterpretationLog(); finishAction('screenshot'); diff --git a/src/context/browserSteps.tsx b/src/context/browserSteps.tsx index db0eb239c..6d8b8f07e 100644 --- a/src/context/browserSteps.tsx +++ b/src/context/browserSteps.tsx @@ -6,12 +6,14 @@ export interface TextStep { label: string; data: string; selectorObj: SelectorObject; + actionId?: string; // Add actionId to track which action created this step } interface ScreenshotStep { id: number; type: 'screenshot'; fullPage: boolean; + actionId?: string; // Add actionId to track which action created this step } export interface ListStep { @@ -24,6 +26,7 @@ export interface ListStep { selector: string; }; limit?: number; + actionId?: string; // Add actionId to track which action created this step } export type BrowserStep = TextStep | ScreenshotStep | ListStep; @@ -38,15 +41,16 @@ export interface SelectorObject { interface BrowserStepsContextType { browserSteps: BrowserStep[]; - addTextStep: (label: string, data: string, selectorObj: SelectorObject) => void; - addListStep: (listSelector: string, fields: { [key: string]: TextStep }, listId: number, pagination?: { type: string; selector: string }, limit?: number) => void - addScreenshotStep: (fullPage: boolean) => void; + addTextStep: (label: string, data: string, selectorObj: SelectorObject, actionId: string) => void; + addListStep: (listSelector: string, fields: { [key: string]: TextStep }, listId: number, actionId: string, pagination?: { type: string; selector: string }, limit?: number) => void + addScreenshotStep: (fullPage: boolean, actionId: string) => void; deleteBrowserStep: (id: number) => void; updateBrowserTextStepLabel: (id: number, newLabel: string) => void; updateListTextFieldLabel: (listId: number, fieldKey: string, newLabel: string) => void; updateListStepLimit: (listId: number, limit: number) => void; updateListStepData: (listId: number, extractedData: any[]) => void; removeListTextField: (listId: number, fieldKey: string) => void; + deleteStepsByActionId: (actionId: string) => void; // New function to delete steps by actionId } const BrowserStepsContext = createContext(undefined); @@ -55,14 +59,14 @@ export const BrowserStepsProvider: React.FC<{ children: React.ReactNode }> = ({ const [browserSteps, setBrowserSteps] = useState([]); const [discardedFields, setDiscardedFields] = useState>(new Set()); - const addTextStep = (label: string, data: string, selectorObj: SelectorObject) => { + const addTextStep = (label: string, data: string, selectorObj: SelectorObject, actionId: string) => { setBrowserSteps(prevSteps => [ ...prevSteps, - { id: Date.now(), type: 'text', label, data, selectorObj } + { id: Date.now(), type: 'text', label, data, selectorObj, actionId } ]); }; - const addListStep = (listSelector: string, newFields: { [key: string]: TextStep }, listId: number, pagination?: { type: string; selector: string }, limit?: number) => { + const addListStep = (listSelector: string, newFields: { [key: string]: TextStep }, listId: number, actionId: string, pagination?: { type: string; selector: string }, limit?: number) => { setBrowserSteps(prevSteps => { const existingListStepIndex = prevSteps.findIndex(step => step.type === 'list' && step.id === listId); @@ -77,10 +81,14 @@ export const BrowserStepsProvider: React.FC<{ children: React.ReactNode }> = ({ if (existingListStep.fields[key]) { acc[key] = { ...field, - label: existingListStep.fields[key].label + label: existingListStep.fields[key].label, + actionId }; } else { - acc[key] = field; + acc[key] = { + ...field, + actionId + }; } } return acc; @@ -90,22 +98,31 @@ export const BrowserStepsProvider: React.FC<{ children: React.ReactNode }> = ({ ...existingListStep, fields: mergedFields, pagination: pagination || existingListStep.pagination, - limit: limit + limit: limit, + actionId }; return updatedSteps; } else { + const fieldsWithActionId = Object.entries(newFields).reduce((acc, [key, field]) => { + acc[key] = { + ...field, + actionId + }; + return acc; + }, {} as { [key: string]: TextStep }); + return [ ...prevSteps, - { id: listId, type: 'list', listSelector, fields: newFields, pagination, limit } + { id: listId, type: 'list', listSelector, fields: fieldsWithActionId, pagination, limit, actionId } ]; } }); }; - const addScreenshotStep = (fullPage: boolean) => { + const addScreenshotStep = (fullPage: boolean, actionId: string) => { setBrowserSteps(prevSteps => [ ...prevSteps, - { id: Date.now(), type: 'screenshot', fullPage } + { id: Date.now(), type: 'screenshot', fullPage, actionId } ]); }; @@ -113,6 +130,10 @@ export const BrowserStepsProvider: React.FC<{ children: React.ReactNode }> = ({ setBrowserSteps(prevSteps => prevSteps.filter(step => step.id !== id)); }; + const deleteStepsByActionId = (actionId: string) => { + setBrowserSteps(prevSteps => prevSteps.filter(step => step.actionId !== actionId)); + }; + const updateBrowserTextStepLabel = (id: number, newLabel: string) => { setBrowserSteps(prevSteps => prevSteps.map(step => @@ -199,6 +220,7 @@ export const BrowserStepsProvider: React.FC<{ children: React.ReactNode }> = ({ updateListStepLimit, updateListStepData, removeListTextField, + deleteStepsByActionId, // Export the new function }}> {children} @@ -211,4 +233,4 @@ export const useBrowserSteps = () => { throw new Error('useBrowserSteps must be used within a BrowserStepsProvider'); } return context; -}; +}; \ No newline at end of file diff --git a/src/context/globalInfo.tsx b/src/context/globalInfo.tsx index 58ee9672c..55f96b068 100644 --- a/src/context/globalInfo.tsx +++ b/src/context/globalInfo.tsx @@ -80,6 +80,12 @@ interface GlobalInfo { }) => void; shouldResetInterpretationLog: boolean; resetInterpretationLog: () => void; + currentTextActionId: string; + setCurrentTextActionId: (actionId: string) => void; + currentListActionId: string; + setCurrentListActionId: (actionId: string) => void; + currentScreenshotActionId: string; + setCurrentScreenshotActionId: (actionId: string) => void; }; class GlobalInfoStore implements Partial { @@ -106,6 +112,9 @@ class GlobalInfoStore implements Partial { hasScrapeSchemaAction: false, }; shouldResetInterpretationLog = false; + currentTextActionId = ''; + currentListActionId = ''; + currentScreenshotActionId = ''; }; const globalInfoStore = new GlobalInfoStore(); @@ -129,6 +138,9 @@ export const GlobalInfoProvider = ({ children }: { children: JSX.Element }) => { const [recordingUrl, setRecordingUrl] = useState(globalInfoStore.recordingUrl); const [currentWorkflowActionsState, setCurrentWorkflowActionsState] = useState(globalInfoStore.currentWorkflowActionsState); const [shouldResetInterpretationLog, setShouldResetInterpretationLog] = useState(globalInfoStore.shouldResetInterpretationLog); + const [currentTextActionId, setCurrentTextActionId] = useState(''); + const [currentListActionId, setCurrentListActionId] = useState(''); + const [currentScreenshotActionId, setCurrentScreenshotActionId] = useState(''); const notify = (severity: 'error' | 'warning' | 'info' | 'success', message: string) => { setNotification({ severity, message, isOpen: true }); @@ -187,6 +199,12 @@ export const GlobalInfoProvider = ({ children }: { children: JSX.Element }) => { setCurrentWorkflowActionsState, shouldResetInterpretationLog, resetInterpretationLog, + currentTextActionId, + setCurrentTextActionId, + currentListActionId, + setCurrentListActionId, + currentScreenshotActionId, + setCurrentScreenshotActionId, }} > {children}