diff --git a/src/components/browser/BrowserRecordingSave.tsx b/src/components/browser/BrowserRecordingSave.tsx index 5beed5d03..7172dbf95 100644 --- a/src/components/browser/BrowserRecordingSave.tsx +++ b/src/components/browser/BrowserRecordingSave.tsx @@ -1,5 +1,5 @@ import React, { useState } from 'react' -import { Grid, Button, Box, Typography, IconButton, Menu, MenuItem, ListItemText } from '@mui/material'; +import { Grid, Button, Box, Typography, IconButton, Menu, MenuItem, ListItemText, Dialog, DialogTitle, DialogActions, } from '@mui/material'; import { SaveRecording } from "../recorder/SaveRecording"; import { useGlobalInfoStore } from '../../context/globalInfo'; import { useActionContext } from '../../context/browserActions'; @@ -20,9 +20,9 @@ const BrowserRecordingSave = () => { const { socket } = useSocketStore(); - const { - stopGetText, - stopGetList, + const { + stopGetText, + stopGetList, stopGetScreenshot, stopPaginationMode, stopLimitMode, @@ -45,7 +45,7 @@ const BrowserRecordingSave = () => { timestamp: Date.now() }; window.sessionStorage.setItem('pendingNotification', JSON.stringify(notificationData)); - + if (window.opener) { window.opener.postMessage({ type: 'recording-notification', @@ -57,9 +57,9 @@ const BrowserRecordingSave = () => { timestamp: Date.now() }, '*'); } - + setBrowserId(null); - + window.close(); stopRecording(browserId).catch((error) => { @@ -74,7 +74,7 @@ const BrowserRecordingSave = () => { stopGetScreenshot(); stopPaginationMode(); stopLimitMode(); - + setShowLimitOptions(false); setShowPaginationOptions(false); setCaptureStage('initial'); @@ -97,7 +97,7 @@ const BrowserRecordingSave = () => { browserSteps.forEach(step => { deleteBrowserStep(step.id); }); - + if (socket) { socket?.emit('new-recording'); socket.emit('input:url', initialUrl); @@ -119,7 +119,7 @@ const BrowserRecordingSave = () => { }; const handleClick = (event: any) => { - setAnchorEl(event.currentTarget); + setAnchorEl(event.currentTarget); }; const handleClose = () => { @@ -183,19 +183,39 @@ const BrowserRecordingSave = () => { - setOpenDiscardModal(false)} modalStyle={modalStyle}> - - {t('browser_recording.modal.confirm_discard')} - - - {t('right_panel.buttons.discard')} - - setOpenDiscardModal(false)} variant="outlined"> - {t('right_panel.buttons.cancel')} - - - - + setOpenDiscardModal(false)} + maxWidth="xs" + fullWidth + PaperProps={{ + sx: { + p: 0, + borderRadius: 2, + border: "none" + } + }} + > + + {t('browser_recording.modal.confirm_discard')} + + + + setOpenDiscardModal(false)} + color="inherit" + > + {t('right_panel.buttons.cancel')} + + + {t('right_panel.buttons.discard')} + + + setOpenResetModal(false)} modalStyle={modalStyle}> @@ -204,9 +224,9 @@ const BrowserRecordingSave = () => { {t('browser_recording.modal.reset_warning')} - {t('right_panel.buttons.confirm_reset')} diff --git a/src/components/recorder/SaveRecording.tsx b/src/components/recorder/SaveRecording.tsx index cbfa7ae84..a6a43ffc6 100644 --- a/src/components/recorder/SaveRecording.tsx +++ b/src/components/recorder/SaveRecording.tsx @@ -1,11 +1,10 @@ import React, { useCallback, useEffect, useState, useContext } from 'react'; -import { Button, Box, LinearProgress, Tooltip } from "@mui/material"; -import { GenericModal } from "../ui/GenericModal"; +import { Button, Box, LinearProgress, Tooltip, Dialog, DialogTitle, DialogContent } from "@mui/material"; import { stopRecording } from "../../api/recording"; import { useGlobalInfoStore } from "../../context/globalInfo"; import { AuthContext } from '../../context/auth'; import { useSocketStore } from "../../context/socket"; -import { TextField, Typography } from "@mui/material"; +import { TextField } from "@mui/material"; import { useNavigate } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; @@ -48,7 +47,7 @@ export const SaveRecording = ({ fileName }: SaveRecordingProps) => { const handleFinishClick = () => { const { hasScrapeListAction, hasScreenshotAction, hasScrapeSchemaAction } = currentWorkflowActionsState; const hasAnyAction = hasScrapeListAction || hasScreenshotAction || hasScrapeSchemaAction; - + if (!hasAnyAction) { notify('warning', t('save_recording.errors.no_actions_performed')); return; @@ -63,7 +62,7 @@ export const SaveRecording = ({ fileName }: SaveRecordingProps) => { const exitRecording = useCallback(async (data?: { actionType: string }) => { let successMessage = t('save_recording.notifications.save_success'); - + if (data && data.actionType) { if (data.actionType === 'retrained') { successMessage = t('save_recording.notifications.retrain_success'); @@ -73,31 +72,31 @@ export const SaveRecording = ({ fileName }: SaveRecordingProps) => { successMessage = t('save_recording.notifications.save_error'); } } - + const notificationData = { type: data?.actionType === 'error' ? 'error' : 'success', message: successMessage, timestamp: Date.now() }; window.sessionStorage.setItem('pendingNotification', JSON.stringify(notificationData)); - + if (window.opener) { window.opener.postMessage({ type: 'recording-notification', notification: notificationData }, '*'); - + window.opener.postMessage({ type: 'session-data-clear', timestamp: Date.now() }, '*'); } - + if (browserId) { await stopRecording(browserId); } setBrowserId(null); - + window.close(); }, [setBrowserId, browserId, t]); @@ -107,7 +106,7 @@ export const SaveRecording = ({ fileName }: SaveRecordingProps) => { if (user) { const { hasScrapeListAction, hasScreenshotAction, hasScrapeSchemaAction } = currentWorkflowActionsState; const hasAnyAction = hasScrapeListAction || hasScreenshotAction || hasScrapeSchemaAction; - + if (!hasAnyAction) { notify('warning', t('save_recording.errors.no_actions_performed')); return; @@ -160,30 +159,54 @@ export const SaveRecording = ({ fileName }: SaveRecordingProps) => { {t('right_panel.buttons.finish')} - setOpenModal(false)} modalStyle={modalStyle}> - - {t('save_recording.title')} - - - {t('save_recording.buttons.save')} - - {waitingForSave && - - - - - + setOpenModal(false)} + maxWidth="xs" + fullWidth + PaperProps={{ + sx: { + p: 0, + borderRadius: 2 } - - + }} + > + + {t('save_recording.title')} + + + + + + + + {t('save_recording.buttons.save')} + + + {waitingForSave && ( + + + + + + )} + + + ); } diff --git a/src/components/robot/RecordingsTable.tsx b/src/components/robot/RecordingsTable.tsx index f1e9bbe02..dfb69de00 100644 --- a/src/components/robot/RecordingsTable.tsx +++ b/src/components/robot/RecordingsTable.tsx @@ -23,6 +23,11 @@ import { CircularProgress, FormControlLabel, Checkbox, + Dialog, + DialogTitle, + DialogContent, + DialogContentText, + DialogActions, } from "@mui/material"; import { Schedule, @@ -94,7 +99,7 @@ const LoadingRobotRow = memo(({ row, columns }: any) => { } else if (column.id === 'interpret') { return ( - - + - ); } else { @@ -181,7 +186,7 @@ export const RecordingsTable = ({ handleSettingsRecording, handleEditRobot, handleDuplicateRobot, - }: RecordingsTableProps) => { +}: RecordingsTableProps) => { const { t } = useTranslation(); const theme = useTheme(); const [page, setPage] = React.useState(0); @@ -227,11 +232,11 @@ export const RecordingsTable = ({ const notificationData = event.data.notification; if (notificationData) { notify(notificationData.type, notificationData.message); - - if ((notificationData.type === 'success' && - (notificationData.message.includes('saved') || notificationData.message.includes('retrained'))) || - (notificationData.type === 'warning' && - notificationData.message.includes('terminated'))) { + + if ((notificationData.type === 'success' && + (notificationData.message.includes('saved') || notificationData.message.includes('retrained'))) || + (notificationData.type === 'warning' && + notificationData.message.includes('terminated'))) { setRerenderRobots(true); } } @@ -248,9 +253,9 @@ export const RecordingsTable = ({ window.sessionStorage.removeItem('initialUrl'); } }; - + window.addEventListener('message', handleMessage); - + return () => { window.removeEventListener('message', handleMessage); }; @@ -325,7 +330,7 @@ export const RecordingsTable = ({ timestamp: Date.now() }; window.sessionStorage.setItem('recordingTabCloseMessage', JSON.stringify(closeMessage)); - + if (window.openedRecordingWindow && !window.openedRecordingWindow.closed) { try { window.openedRecordingWindow.close(); @@ -339,10 +344,10 @@ export const RecordingsTable = ({ if (activeBrowserId) { await stopRecording(activeBrowserId); notify('warning', t('browser_recording.notifications.terminated')); - + notifyRecordingTabsToClose(activeBrowserId); } - + setWarningModalOpen(false); setModalOpen(true); }; @@ -350,31 +355,31 @@ export const RecordingsTable = ({ const handleRetrainRobot = useCallback(async (id: string, name: string) => { const robot = rows.find(row => row.id === id); let targetUrl; - + if (robot?.content?.workflow && robot.content.workflow.length > 0) { const lastPair = robot.content.workflow[robot.content.workflow.length - 1]; - + if (lastPair?.what) { if (Array.isArray(lastPair.what)) { - const gotoAction = lastPair.what.find((action: any) => + const gotoAction = lastPair.what.find((action: any) => action && typeof action === 'object' && 'action' in action && action.action === "goto" ) as any; - + if (gotoAction?.args?.[0]) { targetUrl = gotoAction.args[0]; } } } } - + if (targetUrl) { setInitialUrl(targetUrl); setRecordingUrl(targetUrl); window.sessionStorage.setItem('initialUrl', targetUrl); } - + const canCreateRecording = await canCreateBrowserInState("recording"); - + if (!canCreateRecording) { const activeBrowserId = await getActiveBrowserId(); if (activeBrowserId) { @@ -384,45 +389,45 @@ export const RecordingsTable = ({ notify('warning', t('recordingtable.notifications.browser_limit_warning')); } } else { - startRetrainRecording(id, name, targetUrl); + startRetrainRecording(id, name, targetUrl); } }, [rows, setInitialUrl, setRecordingUrl]); const startRetrainRecording = (id: string, name: string, url?: string) => { setBrowserId('new-recording'); - setRecordingName(name); - setRecordingId(id); - + setRecordingName(name); + setRecordingId(id); + window.sessionStorage.setItem('browserId', 'new-recording'); window.sessionStorage.setItem('robotToRetrain', id); window.sessionStorage.setItem('robotName', name); - + window.sessionStorage.setItem('recordingUrl', url || recordingUrl); - + const sessionId = Date.now().toString(); window.sessionStorage.setItem('recordingSessionId', sessionId); - + window.openedRecordingWindow = window.open(`/recording-setup?session=${sessionId}`, '_blank'); - + window.sessionStorage.setItem('nextTabIsRecording', 'true'); }; const startRecording = () => { setModalOpen(false); - + // Set local state setBrowserId('new-recording'); setRecordingName(''); setRecordingId(''); - + window.sessionStorage.setItem('browserId', 'new-recording'); - + const sessionId = Date.now().toString(); window.sessionStorage.setItem('recordingSessionId', sessionId); window.sessionStorage.setItem('recordingUrl', recordingUrl); - + window.openedRecordingWindow = window.open(`/recording-setup?session=${sessionId}`, '_blank'); - + window.sessionStorage.setItem('nextTabIsRecording', 'true'); }; @@ -442,17 +447,17 @@ export const RecordingsTable = ({ function useDebounce(value: T, delay: number): T { const [debouncedValue, setDebouncedValue] = React.useState(value); - + useEffect(() => { const handler = setTimeout(() => { setDebouncedValue(value); }, delay); - + return () => { clearTimeout(handler); }; }, [value, delay]); - + return debouncedValue; } @@ -472,9 +477,9 @@ export const RecordingsTable = ({ }, [filteredRows, page, rowsPerPage]); const openDeleteConfirm = React.useCallback((id: string) => { - setPendingDeleteId(String(id)); - setDeleteConfirmOpen(true); - }, []); + setPendingDeleteId(String(id)); + setDeleteConfirmOpen(true); + }, []); const confirmDeleteRecording = React.useCallback(async () => { if (!pendingDeleteId) return; @@ -548,13 +553,13 @@ export const RecordingsTable = ({ - + {isFetching ? ( {debouncedSearchTerm ? t('recordingtable.placeholder.search') : t('recordingtable.placeholder.title')} - {debouncedSearchTerm + {debouncedSearchTerm ? t('recordingtable.search_criteria') : t('recordingtable.placeholder.body') } @@ -587,16 +592,16 @@ export const RecordingsTable = ({ <> - - {columns.map((column) => ( - - {column.label} - - ))} - + + {columns.map((column) => ( + + {column.label} + + ))} + {visibleRows.map((row) => ( > )} - setWarningModalOpen(false)} modalStyle={modalStyle}> - - {t('recordingtable.warning_modal.title')} - + setWarningModalOpen(false)} + maxWidth="xs" + fullWidth + PaperProps={{ + sx: { + p: 0, + borderRadius: 2 + } + }} + > + + {t('recordingtable.warning_modal.title')} + + + + {t('recordingtable.warning_modal.message')} - - - - {t('recordingtable.warning_modal.discard_and_create')} - - setWarningModalOpen(false)} - variant="outlined" - > - {t('recordingtable.warning_modal.cancel')} - - - - + + + + setWarningModalOpen(false)} + color='inherit' + > + {t('recordingtable.warning_modal.cancel')} + + + {t('recordingtable.warning_modal.discard_and_create')} + + + setModalOpen(false)} modalStyle={modalStyle}> {t('recordingtable.modal.title')} @@ -679,33 +698,50 @@ export const RecordingsTable = ({ - { setDeleteConfirmOpen(false); setPendingDeleteId(null); }} - modalStyle={{ ...modalStyle, padding: 0, backgroundColor: 'transparent', width: 'auto', maxWidth: '520px' }} + { + setDeleteConfirmOpen(false); + setPendingDeleteId(null); + }} + maxWidth="xs" + fullWidth > + + {t('recordingtable.delete_confirm.title', { + name: pendingRow?.name, + defaultValue: 'Delete {{name}}?' + })} + + + + + {t('recordingtable.delete_confirm.message', { + name: pendingRow?.name, + defaultValue: 'Are you sure you want to delete the robot "{{name}}"?' + })} + + + + + { + setDeleteConfirmOpen(false); + setPendingDeleteId(null); + }} + color="inherit" + > + {t('common.cancel', { defaultValue: 'Cancel' })} + - - - {t('recordingtable.delete_confirm.title', { name: pendingRow?.name, defaultValue: 'Delete {{name}}?' })} - - - {t('recordingtable.delete_confirm.message', { - name: pendingRow?.name, - defaultValue: 'Are you sure you want to delete the robot "{{name}}"?' - })} - - - - { setDeleteConfirmOpen(false); setPendingDeleteId(null); }} variant="outlined"> - {t('common.cancel', { defaultValue: 'Cancel' })} - - - {t('common.delete', { defaultValue: 'Delete' })} - - - - + + {t('common.delete', { defaultValue: 'Delete' })} + + + ); } @@ -838,7 +874,6 @@ const OptionsButton = ({ handleRetrain, handleEdit, handleDuplicate, handleDelet const MemoizedTableCell = memo(TableCell); -// Memoized action buttons const MemoizedInterpretButton = memo(InterpretButton); const MemoizedScheduleButton = memo(ScheduleButton); const MemoizedIntegrateButton = memo(IntegrateButton); diff --git a/src/components/robot/pages/RobotCreate.tsx b/src/components/robot/pages/RobotCreate.tsx index 992af17d7..185a40585 100644 --- a/src/components/robot/pages/RobotCreate.tsx +++ b/src/components/robot/pages/RobotCreate.tsx @@ -18,6 +18,10 @@ import { Select, MenuItem, InputLabel, + Dialog, + DialogTitle, + DialogContent, + DialogActions, Collapse, FormControlLabel } from '@mui/material'; @@ -244,7 +248,7 @@ const RobotCreate: React.FC = () => { setIsLoading(true); try { const formatsForRequest = searchMode === 'discover' ? [] : searchOutputFormats; - + const result = await createSearchRobot( searchRobotName, { @@ -368,7 +372,7 @@ const RobotCreate: React.FC = () => { } }} > - + Recorder Mode @@ -409,7 +413,7 @@ const RobotCreate: React.FC = () => { Beta - + AI Mode @@ -421,190 +425,213 @@ const RobotCreate: React.FC = () => { - {generationMode === 'agent' && ( - - - setExtractRobotName(e.target.value)} - label="Name" - /> - + {generationMode === 'agent' && ( + + + setExtractRobotName(e.target.value)} + label="Name" + /> + + + + setAiPrompt(e.target.value)} + label="Extraction Prompt" + /> + + + + setUrl(e.target.value)} + label="Website URL (Optional)" + /> + + + + + LLM Provider + { + const provider = e.target.value as 'anthropic' | 'openai' | 'ollama'; + setLlmProvider(provider); + setLlmModel('default'); + if (provider === 'ollama') { + setLlmBaseUrl('http://localhost:11434'); + } else { + setLlmBaseUrl(''); + } + }} + > + Ollama (Local) + Anthropic (Claude) + OpenAI (GPT-4) + + + + Model + setLlmModel(e.target.value)} + > + {llmProvider === 'ollama' ? ( + [ + Default (llama3.2-vision), + llama3.2-vision, + llama3.2 + ] + ) : llmProvider === 'anthropic' ? ( + [ + Default (claude-3-5-sonnet), + claude-3-5-sonnet-20241022, + claude-3-opus-20240229 + ] + ) : ( + [ + Default (gpt-4-vision-preview), + gpt-4-vision-preview, + gpt-4o + ] + )} + + + + + {/* API Key for non-Ollama providers */} + {llmProvider !== 'ollama' && ( setAiPrompt(e.target.value)} - label="Extraction Prompt" + type="password" + value={llmApiKey} + onChange={(e) => setLlmApiKey(e.target.value)} + label="API Key (Optional if set in .env)" /> + )} + {llmProvider === 'ollama' && ( setUrl(e.target.value)} - label="Website URL (Optional)" + value={llmBaseUrl} + onChange={(e) => setLlmBaseUrl(e.target.value)} + label="Ollama Base URL (Optional)" /> + )} - - - LLM Provider - { - const provider = e.target.value as 'anthropic' | 'openai' | 'ollama'; - setLlmProvider(provider); - setLlmModel('default'); - if (provider === 'ollama') { - setLlmBaseUrl('http://localhost:11434'); - } else { - setLlmBaseUrl(''); - } - }} - > - Ollama (Local) - Anthropic (Claude) - OpenAI (GPT-4) - - - - - Model - setLlmModel(e.target.value)} - > - {llmProvider === 'ollama' ? ( - [ - Default (llama3.2-vision), - llama3.2-vision, - llama3.2 - ] - ) : llmProvider === 'anthropic' ? ( - [ - Default (claude-3-5-sonnet), - claude-3-5-sonnet-20241022, - claude-3-opus-20240229 - ] - ) : ( - [ - Default (gpt-4-vision-preview), - gpt-4-vision-preview, - gpt-4o - ] - )} - - - + { + // URL is optional for AI mode - it will auto-search if not provided + if (!extractRobotName.trim()) { + notify('error', 'Please enter a robot name'); + return; + } + if (!aiPrompt.trim()) { + notify('error', 'Please enter an extraction prompt'); + return; + } + if (recordings.some(r => r.trim().toLowerCase() === extractRobotName.trim().toLowerCase())) { + notify('error', `A robot with the name "${extractRobotName.trim()}" already exists.`); + return; + } - {/* API Key for non-Ollama providers */} - {llmProvider !== 'ollama' && ( - - setLlmApiKey(e.target.value)} - label="API Key (Optional if set in .env)" - /> - - )} - - {llmProvider === 'ollama' && ( - - setLlmBaseUrl(e.target.value)} - label="Ollama Base URL (Optional)" - /> - - )} + const tempRobotId = `temp-${Date.now()}`; + const robotDisplayName = extractRobotName; - { - // URL is optional for AI mode - it will auto-search if not provided - if (!extractRobotName.trim()) { - notify('error', 'Please enter a robot name'); - return; - } - if (!aiPrompt.trim()) { - notify('error', 'Please enter an extraction prompt'); - return; - } - if (recordings.some(r => r.trim().toLowerCase() === extractRobotName.trim().toLowerCase())) { - notify('error', `A robot with the name "${extractRobotName.trim()}" already exists.`); - return; - } + const optimisticRobot = { + id: tempRobotId, + recording_meta: { + id: tempRobotId, + name: robotDisplayName, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + pairs: 0, + params: [], + type: 'extract', + url: url || '(auto-detecting...)', + }, + recording: { workflow: [] }, + isLoading: true, + isOptimistic: true + }; - const tempRobotId = `temp-${Date.now()}`; - const robotDisplayName = extractRobotName; + addOptimisticRobot(optimisticRobot); - const optimisticRobot = { - id: tempRobotId, - recording_meta: { - id: tempRobotId, - name: robotDisplayName, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - pairs: 0, - params: [], - type: 'extract', - url: url || '(auto-detecting...)', - }, - recording: { workflow: [] }, - isLoading: true, - isOptimistic: true - }; + notify('info', url.trim() + ? `Robot ${robotDisplayName} creation started` + : `Robot ${robotDisplayName} creation started (searching for website...)`); + navigate('/robots'); - addOptimisticRobot(optimisticRobot); + try { + const result = await createLLMRobot( + url.trim() || undefined, + aiPrompt, + llmProvider, + llmModel === 'default' ? undefined : llmModel, + llmApiKey || undefined, + llmBaseUrl || undefined, + extractRobotName + ); - notify('info', url.trim() - ? `Robot ${robotDisplayName} creation started` - : `Robot ${robotDisplayName} creation started (searching for website...)`); - navigate('/robots'); + removeOptimisticRobot(tempRobotId); - try { - const result = await createLLMRobot( - url.trim() || undefined, - aiPrompt, - llmProvider, - llmModel === 'default' ? undefined : llmModel, - llmApiKey || undefined, - llmBaseUrl || undefined, - extractRobotName - ); + if (!result || !result.robot) { + notify('error', 'Failed to create AI robot. Please check your LLM configuration.'); + invalidateRecordings(); + return; + } - removeOptimisticRobot(tempRobotId); + const robotMetaId = result.robot.recording_meta.id; + const robotName = result.robot.recording_meta.name; + + invalidateRecordings(); + notify('success', `${robotName} created successfully!`); + + const optimisticRun = { + id: robotMetaId, + runId: `temp-${Date.now()}`, + status: 'running', + name: robotName, + startedAt: new Date().toISOString(), + finishedAt: '', + robotMetaId: robotMetaId, + log: 'Starting...', + isOptimistic: true + }; - if (!result || !result.robot) { - notify('error', 'Failed to create AI robot. Please check your LLM configuration.'); - invalidateRecordings(); - return; - } + addOptimisticRun(optimisticRun); - const robotMetaId = result.robot.recording_meta.id; - const robotName = result.robot.recording_meta.name; + const runResponse = await createAndRunRecording(robotMetaId, { + maxConcurrency: 1, + maxRepeats: 1, + debug: false + }); invalidateRecordings(); notify('success', `${robotName} created successfully!`); @@ -645,23 +672,29 @@ const RobotCreate: React.FC = () => { invalidateRecordings(); notify('error', error?.message || 'Failed to create and run AI robot'); } - }} - disabled={!extractRobotName.trim() || !aiPrompt.trim() || isLoading} - sx={{ - bgcolor: '#ff00c3', - py: 1.4, - fontSize: '1rem', - textTransform: 'none', - borderRadius: 2 - }} - startIcon={isLoading ? : null} - > - {isLoading ? 'Creating & Running...' : 'Create & Run Robot'} - - - )} + } catch (error: any) { + console.error('Error in AI robot creation:', error); + removeOptimisticRobot(tempRobotId); + invalidateRecordings(); + notify('error', error?.message || 'Failed to create and run AI robot'); + } + }} + disabled={!extractRobotName.trim() || !aiPrompt.trim() || isLoading} + sx={{ + bgcolor: '#ff00c3', + py: 1.4, + fontSize: '1rem', + textTransform: 'none', + borderRadius: 2 + }} + startIcon={isLoading ? : null} + > + {isLoading ? 'Creating & Running...' : 'Create & Run Robot'} + + + )} - {generationMode === 'recorder' && ( + {generationMode === 'recorder' && ( <> { > - )} - + )} + @@ -942,13 +975,13 @@ const RobotCreate: React.FC = () => { setShowCrawlAdvanced(!showCrawlAdvanced)} - sx={{ - textTransform: 'none', - color: '#ff00c3', - }} + onClick={() => setShowCrawlAdvanced(!showCrawlAdvanced)} + sx={{ + textTransform: 'none', + color: '#ff00c3', + }} > - {showCrawlAdvanced ? 'Hide Advanced Options' : 'Advanced Options'} + {showCrawlAdvanced ? 'Hide Advanced Options' : 'Advanced Options'} @@ -1102,38 +1135,38 @@ const RobotCreate: React.FC = () => { - Mode - { - const newMode = e.target.value as 'discover' | 'scrape'; - setSearchMode(newMode); - if (newMode === 'discover') { - setSearchOutputFormats([]); - } else if (searchOutputFormats.length === 0) { - setSearchOutputFormats(DEFAULT_OUTPUT_FORMATS); - } - }} - > - Discover URLs Only - Extract Data from Results - + Mode + { + const newMode = e.target.value as 'discover' | 'scrape'; + setSearchMode(newMode); + if (newMode === 'discover') { + setSearchOutputFormats([]); + } else if (searchOutputFormats.length === 0) { + setSearchOutputFormats(DEFAULT_OUTPUT_FORMATS); + } + }} + > + Discover URLs Only + Extract Data from Results + - Time Range - setSearchTimeRange(e.target.value as 'day' | 'week' | 'month' | 'year' | '')} - > - No Filter - Past 24 Hours - Past Week - Past Month - Past Year - + Time Range + setSearchTimeRange(e.target.value as 'day' | 'week' | 'month' | 'year' | '')} + > + No Filter + Past 24 Hours + Past Week + Past Month + Past Year + @@ -1195,38 +1228,50 @@ const RobotCreate: React.FC = () => { - - { - setWarningModalOpen(false); - setIsLoading(false); - }} modalStyle={modalStyle}> - - {t('recordingtable.warning_modal.title')} - + { + setWarningModalOpen(false); + setIsLoading(false); + }} + maxWidth="xs" + fullWidth + PaperProps={{ + sx: { + p: 0, + borderRadius: 2 + } + }} + > + + {t('recordingtable.warning_modal.title')} + + + + {t('recordingtable.warning_modal.message')} + - - - {t('recordingtable.warning_modal.discard_and_create')} - - { - setWarningModalOpen(false); - setIsLoading(false); - }} - variant="outlined" - > - {t('recordingtable.warning_modal.cancel')} - - - - - + + { + setWarningModalOpen(false); + setIsLoading(false); + }} + color="inherit" + > + {t('recordingtable.warning_modal.cancel')} + + + {t('recordingtable.warning_modal.discard_and_create')} + + + ); diff --git a/src/components/run/ColapsibleRow.tsx b/src/components/run/ColapsibleRow.tsx index cbb6860c0..9d1e9c7b1 100644 --- a/src/components/run/ColapsibleRow.tsx +++ b/src/components/run/ColapsibleRow.tsx @@ -2,7 +2,12 @@ import { useEffect, useRef, useState } from "react"; import * as React from "react"; import TableRow from "@mui/material/TableRow"; import TableCell from "@mui/material/TableCell"; -import { Box, Collapse, IconButton, Typography, Chip, TextField } from "@mui/material"; +import { + Box, Collapse, IconButton, Typography, Chip, TextField, Dialog, DialogTitle, + DialogContent, + DialogContentText, + DialogActions, +} from "@mui/material"; import { Button } from "@mui/material"; import { DeleteForever, KeyboardArrowDown, KeyboardArrowUp, Settings } from "@mui/icons-material"; import { deleteRunFromStorage } from "../../api/storage"; @@ -250,7 +255,7 @@ export const CollapsibleRow = ({ row, handleDelete, isOpen, onToggleExpanded, cu - setDeleteOpen(false)} modalStyle={{ ...modalStyle, padding: 0, backgroundColor: 'transparent', width: 'auto', maxWidth: '520px' }}> - - - {t('runs_table.delete_confirm.title', { - name: row.name, - defaultValue: 'Delete run "{{name}}"?' - })} - - + setDeleteOpen(false)} + maxWidth="xs" + fullWidth + PaperProps={{ + sx: { + p: 0, + backgroundColor: theme.palette.mode === 'dark' + ? theme.palette.grey[900] + : theme.palette.background.paper, + borderRadius: 2, + width: { xs: '90vw', sm: '460px', md: '420px' }, + maxWidth: '90vw', + boxSizing: 'border-box' + } + }} + > + + {t('runs_table.delete_confirm.title', { + name: row.name, + defaultValue: 'Delete run "{{name}}"?' + })} + + + + {t('runs_table.delete_confirm.message', { name: row.name, defaultValue: 'Are you sure you want to delete the run "{{name}}"?' })} - - - setDeleteOpen(false)} variant="outlined"> - {t('common.cancel', { defaultValue: 'Cancel' })} - - - {t('common.delete', { defaultValue: 'Delete' })} - - - - + + + + + setDeleteOpen(false)} + > + {t('common.cancel', { defaultValue: 'Cancel' })} + + + + {t('common.delete', { defaultValue: 'Delete' })} + + + ); }