diff --git a/public/locales/de.json b/public/locales/de.json index b9b4185b1..c696c14d3 100644 --- a/public/locales/de.json +++ b/public/locales/de.json @@ -41,6 +41,12 @@ "notifications": { "delete_warning": "Roboter kann nicht gelöscht werden, da zugehörige Ausführungen vorhanden sind", "delete_success": "Roboter erfolgreich gelöscht" + }, + "delete_confirmation": { + "title": "Aufnahme löschen", + "message": "Sind Sie sicher, dass Sie die Aufnahme {{name}} löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.", + "cancel": "Abbrechen", + "confirm": "Löschen" } }, "mainmenu": { diff --git a/public/locales/en.json b/public/locales/en.json index bd8acce38..3e155a0f2 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -1,502 +1,510 @@ { - "login": { - "title": "Welcome Back!", - "email": "Enter Work Email", - "password": "Password", - "button": "Login", - "loading": "Loading", - "register_prompt": "Don't have an account?", - "register_link": "Register", - "welcome_notification": "Welcome to Maxun!", - "error_notification": "Login Failed. Please try again." - }, - "register": { - "title": "Register Account", - "email": "Enter Work Email", - "password": "Password", - "button": "Register", - "loading": "Loading", - "register_prompt": "Already have an account?", - "login_link": "Login", - "welcome_notification": "Welcome to Maxun!", - "error_notification": "Registeration Failed. Please try again." - }, - "recordingtable":{ - "run": "Run", - "name": "Name", - "schedule": "Schedule", - "integrate": "Integrate", - "settings": "Settings", - "options": "Options", - "heading":"My Robots", - "new":"Create Robot", - "modal":{ - "title":"Enter the URL", - "label":"URL", - "button":"Start Recording" - }, - "edit":"Edit", - "delete":"Delete", - "duplicate":"Duplicate", - "search":"Search Robots...", - "notifications": { - "delete_warning": "Cannot delete robot as it has associated runs", - "delete_success": "Robot deleted successfully" - } + "login": { + "title": "Welcome Back!", + "email": "Email", + "password": "Password", + "button": "Login", + "loading": "Loading", + "register_prompt": "Don't have an account?", + "register_link": "Register", + "welcome_notification": "Welcome to Maxun!", + "error_notification": "Login Failed. Please try again." + }, + "register": { + "title": "Register Account", + "email": "Email", + "password": "Password", + "button": "Register", + "loading": "Loading", + "register_prompt": "Already have an account?", + "login_link": "Login", + "welcome_notification": "Welcome to Maxun!", + "error_notification": "Registeration Failed. Please try again." + }, + "recordingtable": { + "run": "Run", + "name": "Name", + "schedule": "Schedule", + "integrate": "Integrate", + "settings": "Settings", + "options": "Options", + "heading": "My Robots", + "new": "Create Robot", + "modal": { + "title": "Enter the URL", + "label": "URL", + "button": "Start Recording" }, - "mainmenu":{ - "recordings": "Robots", - "runs": "Runs", - "proxy": "Proxy", - "apikey": "API Key", - "feedback":"Join Maxun Cloud", - "apidocs":"Website To API" - }, - "runstable":{ - "runs":"All Runs", - "runStatus":"Status", - "runName":"Name", - "startedAt":"Started At", - "finishedAt":"Finished At", - "delete":"Delete", - "settings":"Settings", - "search":"Search Runs...", - "notifications": { - "no_runs": "No runs found. Please try again.", - "delete_success": "Run deleted successfully" - } + "edit": "Edit", + "delete": "Delete", + "duplicate": "Duplicate", + "search": "Search Robots...", + "notifications": { + "delete_warning": "Cannot delete robot as it has associated runs", + "delete_success": "Robot deleted successfully" + } + ,"delete_confirmation": { + "title": "Delete Recording", + "message": "Are you sure you want to delete the recording {{name}}? This action cannot be undone.", + "cancel": "Cancel", + "confirm": "Delete" + } + }, + "mainmenu": { + "recordings": "Robots", + "runs": "Runs", + "proxy": "Proxy", + "apikey": "API Key", + "feedback": "Join Maxun Cloud", + "apidocs": "Website To API" + }, + "runstable": { + "runs": "All Runs", + "runStatus": "Status", + "runName": "Name", + "startedAt": "Started At", + "finishedAt": "Finished At", + "delete": "Delete", + "settings": "Settings", + "search": "Search Runs...", + "notifications": { + "no_runs": "No runs found. Please try again.", + "delete_success": "Run deleted successfully" + } + }, + "proxy": { + "title": "Proxy Configuration", + "tab_standard": "Standard Proxy", + "tab_rotation": "Automatic Proxy Rotation", + "server_url": "Proxy Server URL", + "server_url_helper": "Proxy to be used for all robots. HTTP and SOCKS proxies are supported. Example http://myproxy.com:3128 or socks5://myproxy.com:3128. Short form myproxy.com:3128 is considered an HTTP proxy.", + "requires_auth": "Requires Authentication?", + "username": "Username", + "password": "Password", + "add_proxy": "Add Proxy", + "test_proxy": "Test Proxy", + "remove_proxy": "Remove Proxy", + "table": { + "proxy_url": "Proxy URL", + "requires_auth": "Requires Authentication" }, - "proxy": { - "title": "Proxy Configuration", - "tab_standard": "Standard Proxy", - "tab_rotation": "Automatic Proxy Rotation", - "server_url": "Proxy Server URL", - "server_url_helper": "Proxy to be used for all robots. HTTP and SOCKS proxies are supported. Example http://myproxy.com:3128 or socks5://myproxy.com:3128. Short form myproxy.com:3128 is considered an HTTP proxy.", - "requires_auth": "Requires Authentication?", - "username": "Username", - "password": "Password", - "add_proxy": "Add Proxy", - "test_proxy": "Test Proxy", - "remove_proxy": "Remove Proxy", - "table": { - "proxy_url": "Proxy URL", - "requires_auth": "Requires Authentication" - }, - "coming_soon": "Coming Soon - In Open Source (Basic Rotation) & Cloud (Advanced Rotation). If you don't want to manage the infrastructure, join our cloud waitlist to get early access.", - "join_waitlist": "Join Maxun Cloud Waitlist", - "alert": { - "title": "If your proxy requires a username and password, always provide them separately from the proxy URL.", - "right_way": "The right way", - "wrong_way": "The wrong way", - "proxy_url": "Proxy URL:", - "username": "Username:", - "password": "Password:" - }, - "notifications": { - "config_success": "Proxy configuration submitted successfully", - "config_error": "Failed to submit proxy configuration. Try again.", - "test_success": "Proxy configuration is working", - "test_error": "Failed to test proxy configuration. Try again.", - "fetch_success": "Proxy configuration fetched successfully", - "remove_success": "Proxy configuration removed successfully", - "remove_error": "Failed to remove proxy configuration. Try again." - } + "coming_soon": "Coming Soon - In Open Source (Basic Rotation) & Cloud (Advanced Rotation). If you don't want to manage the infrastructure, join our cloud waitlist to get early access.", + "join_waitlist": "Join Maxun Cloud Waitlist", + "alert": { + "title": "If your proxy requires a username and password, always provide them separately from the proxy URL.", + "right_way": "The right way", + "wrong_way": "The wrong way", + "proxy_url": "Proxy URL:", + "username": "Username:", + "password": "Password:" }, - "apikey": { - "title": "Manage Your API Key", - "default_name": "Maxun API Key", - "table": { - "name": "API Key Name", - "key": "API Key", - "actions": "Actions" - }, - "actions": { - "copy": "Copy", - "show": "Show", - "hide": "Hide", - "delete": "Delete" - }, - "no_key_message": "You haven't generated an API key yet.", - "generate_button": "Generate API Key", - "notifications": { - "fetch_error": "Failed to fetch API Key - ${error}", - "generate_success": "Generated API Key successfully", - "generate_error": "Failed to generate API Key - ${error}", - "delete_success": "API Key deleted successfully", - "delete_error": "Failed to delete API Key - ${error}", - "copy_success": "Copied API Key successfully" - } + "notifications": { + "config_success": "Proxy configuration submitted successfully", + "config_error": "Failed to submit proxy configuration. Try again.", + "test_success": "Proxy configuration is working", + "test_error": "Failed to test proxy configuration. Try again.", + "fetch_success": "Proxy configuration fetched successfully", + "remove_success": "Proxy configuration removed successfully", + "remove_error": "Failed to remove proxy configuration. Try again." + } + }, + "apikey": { + "title": "Manage Your API Key", + "default_name": "Maxun API Key", + "table": { + "name": "API Key Name", + "key": "API Key", + "actions": "Actions" }, - "action_description": { - "text": { - "title": "Capture Text", - "description": "Hover over the texts you want to extract and click to select them" - }, - "screenshot": { - "title": "Capture Screenshot", - "description": "Capture a partial or full page screenshot of the current page." - }, - "list": { - "title": "Capture List", - "description": "Hover over the list you want to extract. Once selected, you can hover over all texts inside the list you selected. Click to select them." - }, - "default": { - "title": "What data do you want to extract?", - "description": "A robot is designed to perform one action at a time. You can choose any of the options below." - }, - "list_stages": { - "initial": "Select the list you want to extract along with the texts inside it", - "pagination": "Select how the robot can capture the rest of the list", - "limit": "Choose the number of items to extract", - "complete": "Capture is complete" - } + "actions": { + "copy": "Copy", + "show": "Show", + "hide": "Hide", + "delete": "Delete" }, - "right_panel": { - "buttons": { - "capture_list": "Capture List", - "capture_text": "Capture Text", - "capture_screenshot": "Capture Screenshot", - "confirm": "Confirm", - "discard": "Discard", - "confirm_capture": "Confirm Capture", - "confirm_pagination": "Confirm", - "confirm_limit": "Confirm", - "finish_capture": "Finish Capture", - "back": "Back", - "finish": "Finish", - "cancel": "Cancel", - "delete": "Delete" - }, - "screenshot": { - "capture_fullpage": "Capture Fullpage", - "capture_visible": "Capture Visible Part", - "display_fullpage": "Take Fullpage Screenshot", - "display_visible": "Take Visible Part Screenshot" - }, - "pagination": { - "title": "How can we find the next list item on the page?", - "click_next": "Click on next to navigate to the next page", - "click_load_more": "Click on load more to load more items", - "scroll_down": "Scroll down to load more items", - "scroll_up": "Scroll up to load more items", - "none": "No more items to load" - }, - "limit": { - "title": "What is the maximum number of rows you want to extract?", - "custom": "Custom", - "enter_number": "Enter number" - }, - "fields": { - "label": "Label", - "data": "Data", - "field_label": "Field Label", - "field_data": "Field Data" - }, - "messages": { - "list_selected": "List Selected Successfully" - }, - "errors": { - "select_pagination": "Please select a pagination type.", - "select_pagination_element": "Please select the pagination element first.", - "select_limit": "Please select a limit or enter a custom limit.", - "invalid_limit": "Please enter a valid limit.", - "confirm_text_fields": "Please confirm all text fields", - "unable_create_settings": "Unable to create list settings. Make sure you have defined a field for the list.", - "capture_text_discarded": "Capture Text Discarded", - "capture_list_discarded": "Capture List Discarded" - } + "no_key_message": "You haven't generated an API key yet.", + "generate_button": "Generate API Key", + "notifications": { + "fetch_error": "Failed to fetch API Key - ${error}", + "generate_success": "Generated API Key successfully", + "generate_error": "Failed to generate API Key - ${error}", + "delete_success": "API Key deleted successfully", + "delete_error": "Failed to delete API Key - ${error}", + "copy_success": "Copied API Key successfully" + } + }, + "action_description": { + "text": { + "title": "Capture Text", + "description": "Hover over the texts you want to extract and click to select them" }, - "save_recording": { - "title": "Save Robot", - "robot_name": "Robot Name", - "buttons": { - "save": "Save", - "confirm": "Confirm" - }, - "notifications": { - "save_success": "Robot saved successfully" - }, - "errors": { - "user_not_logged": "User not logged in. Cannot save recording.", - "exists_warning": "Robot with this name already exists, please confirm the Robot's overwrite." - }, - "tooltips": { - "saving": "Optimizing and saving the workflow" - } + "screenshot": { + "title": "Capture Screenshot", + "description": "Capture a partial or full page screenshot of the current page." }, - "browser_recording": { - "modal": { - "confirm_discard": "Are you sure you want to discard the recording?" - }, - "notifications": { - "terminated": "Current Recording was terminated" - } + "list": { + "title": "Capture List", + "description": "Hover over the list you want to extract. Once selected, you can hover over all texts inside the list you selected. Click to select them." }, - "interpretation_log": { - "titles": { - "output_preview": "Output Data Preview", - "screenshot": "Screenshot" - }, - "messages": { - "additional_rows": "Additional rows of data will be extracted once you finish recording.", - "successful_training": "You've successfully trained the robot to perform actions! Click on the button below to get a preview of the data your robot will extract.", - "no_selection": "It looks like you have not selected anything for extraction yet. Once you do, the robot will show a preview of your selections here." - }, - "data_sections": { - "binary_received": "---------- Binary output data received ----------", - "serializable_received": "---------- Serializable output data received ----------", - "mimetype": "mimetype: ", - "image_below": "Image is rendered below:", - "separator": "--------------------------------------------------" - }, - "notifications": { - "reset_success": "Output Preview reset successfully" - } + "default": { + "title": "What data do you want to extract?", + "description": "A robot is designed to perform one action at a time. You can choose any of the options below." }, - "interpretation_buttons": { - "buttons": { - "preview": "Get Preview of Output Data", - "reset": "Reset", - "yes": "Yes", - "no": "No" - }, - "messages": { - "extracting": "Extracting data...please wait for 10secs to 1min", - "restart_required": "Please restart the interpretation after updating the recording", - "run_finished": "Run finished", - "run_failed": "Run failed to start" - }, - "modal": { - "use_previous": "Do you want to use your previous selection as a condition for performing this action?", - "previous_action": "Your previous action was: ", - "element_text": "on an element with text " - } + "list_stages": { + "initial": "Select the list you want to extract along with the texts inside it", + "pagination": "Select how the robot can capture the rest of the list", + "limit": "Choose the number of items to extract", + "complete": "Capture is complete" + } + }, + "right_panel": { + "buttons": { + "capture_list": "Capture List", + "capture_text": "Capture Text", + "capture_screenshot": "Capture Screenshot", + "confirm": "Confirm", + "discard": "Discard", + "confirm_capture": "Confirm Capture", + "confirm_pagination": "Confirm Pagination", + "confirm_limit": "Confirm Limit", + "finish_capture": "Finish Capture", + "finish": "Finish", + "cancel": "Cancel" }, - "recording_page": { - "loader": { - "browser_startup": "Spinning up a browser...Navigating to {{url}}" - } + "screenshot": { + "capture_fullpage": "Capture Fullpage", + "capture_visible": "Capture Visible Part", + "display_fullpage": "Take Fullpage Screenshot", + "display_visible": "Take Visible Part Screenshot" }, - "integration_settings": { - "title": "Integrate with Google Sheet", - "descriptions": { - "sync_info": "If you enable this option, every time this robot runs a task successfully, its captured data will be appended to your Google Sheet.", - "authenticated_as": "Authenticated as: {{email}}" - }, - "alerts": { - "success": { - "title": "Google Sheet Integrated Successfully.", - "content": "Every time this robot creates a successful run, its captured data is appended to your {{sheetName}} Google Sheet. You can check the data updates", - "here": "here", - "note": "Note:", - "sync_limitation": "The data extracted before integrating with Google Sheets will not be synced in the Google Sheet. Only the data extracted after the integration will be synced." - } - }, - "buttons": { - "authenticate": "Authenticate with Google", - "fetch_sheets": "Fetch Google Spreadsheets", - "remove_integration": "Remove Integration", - "submit": "Submit" - }, - "fields": { - "select_sheet": "Select Google Sheet", - "selected_sheet": "Selected Sheet: {{name}} (ID: {{id}})" - }, - "errors": { - "auth_error": "Error authenticating with Google", - "fetch_error": "Error fetching spreadsheet files: {{message}}", - "update_error": "Error updating Google Sheet ID: {{message}}", - "remove_error": "Error removing Google Sheets integration: {{message}}" - }, - "notifications": { - "sheet_selected": "Google Sheet selected successfully" - } + "pagination": { + "title": "How can we find the next list item on the page?", + "click_next": "Click on next to navigate to the next page", + "click_load_more": "Click on load more to load more items", + "scroll_down": "Scroll down to load more items", + "scroll_up": "Scroll up to load more items", + "none": "No more items to load" }, - "robot_duplication": { - "title": "Duplicate Robot", - "descriptions": { - "purpose": "Robot duplication is useful to extract data from pages with the same structure.", - "example": "Example: If you've created a robot for {{url1}}, you can duplicate it to scrape similar pages like {{url2}} without training a robot from scratch.", - "warning": "⚠️ Ensure the new page has the same structure as the original page." - }, - "fields": { - "target_url": "Robot Target URL" - }, - "buttons": { - "duplicate": "Duplicate Robot", - "cancel": "Cancel" - }, - "notifications": { - "robot_not_found": "Could not find robot details. Please try again.", - "url_required": "Target URL is required.", - "duplicate_success": "Robot duplicated successfully.", - "duplicate_error": "Failed to update the Target URL. Please try again.", - "unknown_error": "An error occurred while updating the Target URL." - } + "limit": { + "title": "What is the maximum number of rows you want to extract?", + "custom": "Custom", + "enter_number": "Enter number" }, - "robot_settings": { - "title": "Robot Settings", - "target_url": "Robot Target URL", - "robot_id": "Robot ID", - "robot_limit": "Robot Limit", - "created_by_user": "Created By User", - "created_at": "Robot Created At", - "errors": { - "robot_not_found": "Could not find robot details. Please try again." - } + "fields": { + "label": "Label", + "data": "Data", + "field_label": "Field Label", + "field_data": "Field Data" }, - "robot_edit": { - "title": "Edit Robot", - "change_name": "Robot Name", - "robot_limit": "Robot Limit", - "save": "Save Changes", - "cancel": "Cancel", - "notifications": { - "update_success": "Robot updated successfully.", - "update_failed": "Failed to update the robot. Please try again.", - "update_error": "An error occurred while updating the robot." - } + "messages": { + "list_selected": "List Selected Successfully" }, - "schedule_settings": { - "title": "Schedule Settings", - "run_every": "Run every", - "start_from": "Start From", - "on_day": "On day", - "at_around": "At around", - "timezone": "Timezone", - "buttons": { - "delete_schedule": "Delete Schedule", - "save_schedule": "Save Schedule", - "cancel": "Cancel" - }, - "labels": { - "in_between": "In Between", - "run_once_every": "Run once every", - "start_from_label": "Start From", - "on_day_of_month": "On Day of the Month", - "on_day": { - "st": "st", - "nd": "nd", - "rd": "rd", - "th": "th" - } + "errors": { + "select_pagination": "Please select a pagination type.", + "select_pagination_element": "Please select the pagination element first.", + "select_limit": "Please select a limit or enter a custom limit.", + "invalid_limit": "Please enter a valid limit.", + "confirm_text_fields": "Please confirm all text fields", + "unable_create_settings": "Unable to create list settings. Make sure you have defined a field for the list.", + "capture_text_discarded": "Capture Text Discarded", + "capture_list_discarded": "Capture List Discarded" + } + }, + "save_recording": { + "title": "Save Robot", + "robot_name": "Robot Name", + "buttons": { + "save": "Save", + "confirm": "Confirm" + }, + "notifications": { + "save_success": "Robot saved successfully" + }, + "errors": { + "user_not_logged": "User not logged in. Cannot save recording.", + "exists_warning": "Robot with this name already exists, please confirm the Robot's overwrite." + }, + "tooltips": { + "saving": "Optimizing and saving the workflow" + } + }, + "browser_recording": { + "modal": { + "confirm_discard": "Are you sure you want to discard the recording?" + }, + "notifications": { + "terminated": "Current Recording was terminated" + } + }, + "interpretation_log": { + "titles": { + "output_preview": "Output Data Preview", + "screenshot": "Screenshot" + }, + "messages": { + "additional_rows": "Additional rows of data will be extracted once you finish recording.", + "successful_training": "You've successfully trained the robot to perform actions! Click on the button below to get a preview of the data your robot will extract.", + "no_selection": "It looks like you have not selected anything for extraction yet. Once you do, the robot will show a preview of your selections here." + }, + "data_sections": { + "binary_received": "---------- Binary output data received ----------", + "serializable_received": "---------- Serializable output data received ----------", + "mimetype": "mimetype: ", + "image_below": "Image is rendered below:", + "separator": "--------------------------------------------------" + }, + "notifications": { + "reset_success": "Output Preview reset successfully" + } + }, + "interpretation_buttons": { + "buttons": { + "preview": "Get Preview of Output Data", + "reset": "Reset", + "yes": "Yes", + "no": "No" + }, + "messages": { + "extracting": "Extracting data...please wait for 10secs to 1min", + "restart_required": "Please restart the interpretation after updating the recording", + "run_finished": "Run finished", + "run_failed": "Run failed to start" + }, + "modal": { + "use_previous": "Do you want to use your previous selection as a condition for performing this action?", + "previous_action": "Your previous action was: ", + "element_text": "on an element with text " + } + }, + "recording_page": { + "loader": { + "browser_startup": "Spinning up a browser...Navigating to {{url}}" + } + }, + "integration_settings": { + "title": "Integrate with Google Sheet", + "descriptions": { + "sync_info": "If you enable this option, every time this robot runs a task successfully, its captured data will be appended to your Google Sheet.", + "authenticated_as": "Authenticated as: {{email}}" + }, + "alerts": { + "success": { + "title": "Google Sheet Integrated Successfully.", + "content": "Every time this robot creates a successful run, its captured data is appended to your {{sheetName}} Google Sheet. You can check the data updates", + "here": "here", + "note": "Note:", + "sync_limitation": "The data extracted before integrating with Google Sheets will not be synced in the Google Sheet. Only the data extracted after the integration will be synced." } }, - "main_page": { - "notifications": { - "interpretation_success": "Interpretation of robot {{name}} succeeded", - "interpretation_failed": "Failed to interpret robot {{name}}", - "run_started": "Running robot: {{name}}", - "run_start_failed": "Failed to run robot: {{name}}", - "schedule_success": "Robot {{name}} scheduled successfully", - "schedule_failed": "Failed to schedule robot {{name}}", - "abort_success": "Interpretation of robot {{name}} aborted successfully", - "abort_failed": "Failed to abort the interpretation of robot {{name}}" - }, - "menu": { - "recordings": "Robots", - "runs": "Runs", - "proxy": "Proxy", - "apikey": "API Key" + "buttons": { + "authenticate": "Authenticate with Google", + "fetch_sheets": "Fetch Google Spreadsheets", + "remove_integration": "Remove Integration", + "submit": "Submit" + }, + "fields": { + "select_sheet": "Select Google Sheet", + "selected_sheet": "Selected Sheet: {{name}} (ID: {{id}})" + }, + "errors": { + "auth_error": "Error authenticating with Google", + "fetch_error": "Error fetching spreadsheet files: {{message}}", + "update_error": "Error updating Google Sheet ID: {{message}}", + "remove_error": "Error removing Google Sheets integration: {{message}}" + }, + "notifications": { + "sheet_selected": "Google Sheet selected successfully" + } + }, + "robot_duplication": { + "title": "Duplicate Robot", + "descriptions": { + "purpose": "Robot duplication is useful to extract data from pages with the same structure.", + "example": "Example: If you've created a robot for {{url1}}, you can duplicate it to scrape similar pages like {{url2}} without training a robot from scratch.", + "warning": "⚠️ Ensure the new page has the same structure as the original page." + }, + "fields": { + "target_url": "Robot Target URL" + }, + "buttons": { + "duplicate": "Duplicate Robot", + "cancel": "Cancel" + }, + "notifications": { + "robot_not_found": "Could not find robot details. Please try again.", + "url_required": "Target URL is required.", + "duplicate_success": "Robot duplicated successfully.", + "duplicate_error": "Failed to update the Target URL. Please try again.", + "unknown_error": "An error occurred while updating the Target URL." + } + }, + "robot_settings": { + "title": "Robot Settings", + "target_url": "Robot Target URL", + "robot_id": "Robot ID", + "robot_limit": "Robot Limit", + "created_by_user": "Created By User", + "created_at": "Robot Created At", + "errors": { + "robot_not_found": "Could not find robot details. Please try again." + } + }, + "robot_edit": { + "title": "Edit Robot", + "change_name": "Change Robot Name", + "robot_limit": "Robot Limit", + "save": "Save Changes", + "cancel": "Cancel", + "notifications": { + "update_success": "Robot updated successfully.", + "update_failed": "Failed to update the robot. Please try again.", + "update_error": "An error occurred while updating the robot." + } + }, + "schedule_settings": { + "title": "Schedule Settings", + "run_every": "Run every", + "start_from": "Start From", + "on_day": "On day", + "at_around": "At around", + "timezone": "Timezone", + "buttons": { + "delete_schedule": "Delete Schedule", + "save_schedule": "Save Schedule", + "cancel": "Cancel" + }, + "labels": { + "in_between": "In Between", + "run_once_every": "Run once every", + "start_from_label": "Start From", + "on_day_of_month": "On Day of the Month", + "on_day": { + "st": "st", + "nd": "nd", + "rd": "rd", + "th": "th" } + } + }, + "main_page": { + "notifications": { + "interpretation_success": "Interpretation of robot {{name}} succeeded", + "interpretation_failed": "Failed to interpret robot {{name}}", + "run_started": "Running robot: {{name}}", + "run_start_failed": "Failed to run robot: {{name}}", + "schedule_success": "Robot {{name}} scheduled successfully", + "schedule_failed": "Failed to schedule robot {{name}}", + "abort_success": "Interpretation of robot {{name}} aborted successfully", + "abort_failed": "Failed to abort the interpretation of robot {{name}}" }, - "browser_window": { - "attribute_modal": { - "title": "Select Attribute", - "notifications": { - "list_select_success": "List has been successfully selected. Please select the text data to extract.", - "pagination_select_success": "Pagination element has been successfully selected." - } - }, - "attribute_options": { - "anchor": { - "text": "Text: {{text}}", - "url": "URL: {{url}}" - }, - "image": { - "alt_text": "Alt Text: {{altText}}", - "image_url": "Image URL: {{imageUrl}}" - }, - "default": { - "text": "Text: {{text}}" - } + "menu": { + "recordings": "Robots", + "runs": "Runs", + "proxy": "Proxy", + "apikey": "API Key" + } + }, + "browser_window": { + "attribute_modal": { + "title": "属性を選択", + "notifications": { + "list_select_success": "リストが正常に選択されました。抽出するテキストデータを選択してください。", + "pagination_select_success": "ページネーション要素が正常に選択されました。" } }, - "runs_table": { - "run_type_chips": { - "manual_run": "Manual Run", - "scheduled_run": "Scheduled Run", - "api": "API", - "unknown_run_type": "Unknown Run Type" + "attribute_options": { + "anchor": { + "text": "テキスト: {{text}}", + "url": "URL: {{url}}" }, - "run_status_chips": { - "success": "Success", - "running": "Running", - "scheduled": "Scheduled", - "failed": "Failed" + "image": { + "alt_text": "代替テキスト: {{altText}}", + "image_url": "画像URL: {{imageUrl}}" }, - "run_settings_modal": { - "title": "Run Settings", - "labels": { - "run_id": "Run ID", - "run_by_user": "Run by User", - "run_by_schedule": "Run by Schedule ID", - "run_by_api": "Run by API", - "run_type": "Run Type" - } + "default": { + "text": "テキスト: {{text}}" } + } + }, + "runs_table": { + "run_type_chips": { + "manual_run": "Manual Run", + "scheduled_run": "Scheduled Run", + "api": "API", + "unknown_run_type": "Unknown Run Type" }, - "run_content": { - "tabs": { - "output_data": "Output Data", - "log": "Log" - }, - "empty_output": "The output is empty.", - "captured_data": { - "title": "Captured Data", - "download_json": "Download as JSON", - "download_csv": "Download as CSV" - }, - "captured_screenshot": { - "title": "Captured Screenshot", - "download": "Download Screenshot", - "render_failed": "The image failed to render" - }, - "buttons": { - "stop": "Stop" + "run_status_chips": { + "success": "Success", + "running": "Running", + "scheduled": "Scheduled", + "failed": "Failed" + }, + "run_settings_modal": { + "title": "Run Settings", + "labels": { + "run_id": "Run ID", + "run_by_user": "Run by User", + "run_by_schedule": "Run by Schedule ID", + "run_by_api": "Run by API", + "run_type": "Run Type" } + } + }, + "run_content": { + "tabs": { + "output_data": "Output Data", + "log": "Log" }, - "navbar": { - "project_name": "Maxun", - "upgrade": { - "button": "Upgrade", - "modal": { - "up_to_date": "🎉 You're up to date!", - "new_version_available": "A new version is available: {{version}}. Upgrade to the latest version for bug fixes, enhancements and new features!", - "view_updates": "View all the new updates", - "view_updates_link": "here", - "tabs": { - "manual_setup": "Manual Setup Upgrade", - "docker_setup": "Docker Compose Setup Upgrade" - } + "empty_output": "The output is empty.", + "captured_data": { + "title": "Captured Data", + "download_json": "Download as JSON", + "download_csv": "Download as CSV" + }, + "captured_screenshot": { + "title": "Captured Screenshot", + "download": "Download Screenshot", + "render_failed": "The image failed to render" + }, + "buttons": { + "stop": "Stop" + } + }, + "navbar": { + "project_name": "Maxun", + "upgrade": { + "button": "Upgrade", + "modal": { + "up_to_date": "🎉 You're up to date!", + "new_version_available": "A new version is available: {{version}}. Upgrade to the latest version for bug fixes, enhancements and new features!", + "view_updates": "View all the new updates", + "view_updates_link": "here", + "tabs": { + "manual_setup": "Manual Setup Upgrade", + "docker_setup": "Docker Compose Setup Upgrade" } - }, - "menu_items": { - "logout": "Logout", - "discord": "Discord", - "youtube": "YouTube", - "twitter": "Twitter (X)", - "language": "Language" - }, - "recording": { - "discard": "Discard" } }, - "language_menu": { - "en": "English", - "es": "Spanish", - "ja": "Japanese", - "zh": "Chinese", - "de": "German" + "menu_items": { + "logout": "Logout", + "discord": "Discord", + "youtube": "YouTube", + "twitter": "Twitter (X)", + "language": "Language" + }, + "recording": { + "discard": "Discard" } -} \ No newline at end of file + }, + "language_menu": { + "en": "English", + "es": "Spanish", + "ja": "Japanese", + "zh": "Chinese", + "de": "German" + } + + + + +} diff --git a/public/locales/es.json b/public/locales/es.json index 942108801..c0210f027 100644 --- a/public/locales/es.json +++ b/public/locales/es.json @@ -42,6 +42,12 @@ "notifications": { "delete_warning": "No se puede eliminar el robot ya que tiene ejecuciones asociadas", "delete_success": "Robot eliminado exitosamente" + }, + "delete_confirmation": { + "title": "Eliminar grabación", + "message": "¿Está seguro de que desea eliminar la grabación {{name}}? Esta acción no se puede deshacer.", + "cancel": "Cancelar", + "confirm": "Eliminar" } }, "mainmenu": { diff --git a/public/locales/ja.json b/public/locales/ja.json index 0bcba967a..de03d8ebc 100644 --- a/public/locales/ja.json +++ b/public/locales/ja.json @@ -42,6 +42,12 @@ "notifications": { "delete_warning": "関連する実行があるため、ロボットを削除できません", "delete_success": "ロボットが正常に削除されました" + }, + "delete_confirmation": { + "title": "記録を削除", + "message": "記録「{{name}}」を削除してもよろしいですか?この操作は取り消せません。", + "cancel": "キャンセル", + "confirm": "削除" } }, "mainmenu": { diff --git a/public/locales/zh.json b/public/locales/zh.json index a19fe4391..50ba0af0a 100644 --- a/public/locales/zh.json +++ b/public/locales/zh.json @@ -42,6 +42,12 @@ "notifications": { "delete_warning": "无法删除机器人,因为它有关联的运行记录", "delete_success": "机器人删除成功" + }, + "delete_confirmation": { + "title": "删除录制", + "message": "您确定要删除录制 {{name}} 吗?此操作无法撤消。", + "cancel": "取消", + "confirm": "删除" } }, "mainmenu": { diff --git a/src/components/robot/RecordingsTable.tsx b/src/components/robot/RecordingsTable.tsx index 3c738dd90..66acc2510 100644 --- a/src/components/robot/RecordingsTable.tsx +++ b/src/components/robot/RecordingsTable.tsx @@ -1,35 +1,48 @@ -import * as React from 'react'; -import { useTranslation } from 'react-i18next'; -import Paper from '@mui/material/Paper'; -import Table from '@mui/material/Table'; -import TableBody from '@mui/material/TableBody'; -import TableCell from '@mui/material/TableCell'; -import TableContainer from '@mui/material/TableContainer'; -import TableHead from '@mui/material/TableHead'; -import TablePagination from '@mui/material/TablePagination'; -import TableRow from '@mui/material/TableRow'; +import * as React from "react"; +import { useTranslation } from "react-i18next"; +import Paper from "@mui/material/Paper"; +import Table from "@mui/material/Table"; +import TableBody from "@mui/material/TableBody"; +import TableCell from "@mui/material/TableCell"; +import TableContainer from "@mui/material/TableContainer"; +import TableHead from "@mui/material/TableHead"; +import TablePagination from "@mui/material/TablePagination"; +import TableRow from "@mui/material/TableRow"; import { useEffect } from "react"; import { WorkflowFile } from "maxun-core"; -import SearchIcon from '@mui/icons-material/Search'; -import { IconButton, Button, Box, Typography, TextField, MenuItem, Menu, ListItemIcon, ListItemText } from "@mui/material"; -import { Schedule, DeleteForever, Edit, PlayCircle, Settings, Power, ContentCopy, MoreHoriz } from "@mui/icons-material"; +import SearchIcon from "@mui/icons-material/Search"; +import { IconButton, Button, Box, Typography, TextField, MenuItem, Menu, ListItemIcon, ListItemText, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle } from "@mui/material" +import { + Schedule, + DeleteForever, + Edit, + PlayCircle, + Settings, + Power, + ContentCopy, + MoreHoriz, +} from "@mui/icons-material"; import { useGlobalInfoStore } from "../../context/globalInfo"; -import { checkRunsForRecording, deleteRecordingFromStorage, getStoredRecordings } from "../../api/storage"; +import { + checkRunsForRecording, + deleteRecordingFromStorage, + getStoredRecordings, +} from "../../api/storage"; import { Add } from "@mui/icons-material"; -import { useNavigate } from 'react-router-dom'; +import { useNavigate } from "react-router-dom"; import { stopRecording } from "../../api/recording"; import { GenericModal } from '../ui/GenericModal'; /** TODO: * 1. allow editing existing robot after persisting browser steps -*/ + */ interface Column { - id: 'interpret' | 'name' | 'options' | 'schedule' | 'integrate' | 'settings'; + id: "interpret" | "name" | "options" | "schedule" | "integrate" | "settings"; label: string; minWidth?: number; - align?: 'right'; + align?: "right"; format?: (value: string) => string; } @@ -45,9 +58,21 @@ interface Data { interface RecordingsTableProps { handleEditRecording: (id: string, fileName: string) => void; handleRunRecording: (id: string, fileName: string, params: string[]) => void; - handleScheduleRecording: (id: string, fileName: string, params: string[]) => void; - handleIntegrateRecording: (id: string, fileName: string, params: string[]) => void; - handleSettingsRecording: (id: string, fileName: string, params: string[]) => void; + handleScheduleRecording: ( + id: string, + fileName: string, + params: string[] + ) => void; + handleIntegrateRecording: ( + id: string, + fileName: string, + params: string[] + ) => void; + handleSettingsRecording: ( + id: string, + fileName: string, + params: string[] + ) => void; handleEditRobot: (id: string, name: string, params: string[]) => void; handleDuplicateRobot: (id: string, name: string, params: string[]) => void; } @@ -58,41 +83,54 @@ export const RecordingsTable = ({ handleEditRecording, handleRunRecording, handl const [rowsPerPage, setRowsPerPage] = React.useState(10); const [rows, setRows] = React.useState([]); const [isModalOpen, setModalOpen] = React.useState(false); - const [searchTerm, setSearchTerm] = React.useState(''); + const [searchTerm, setSearchTerm] = React.useState(""); const columns: readonly Column[] = [ - { id: 'interpret', label: t('recordingtable.run'), minWidth: 80 }, - { id: 'name', label: t('recordingtable.name'), minWidth: 80 }, + { id: "interpret", label: t("recordingtable.run"), minWidth: 80 }, + { id: "name", label: t("recordingtable.name"), minWidth: 80 }, { - id: 'schedule', - label: t('recordingtable.schedule'), + id: "schedule", + label: t("recordingtable.schedule"), minWidth: 80, }, { - id: 'integrate', - label: t('recordingtable.integrate'), + id: "integrate", + label: t("recordingtable.integrate"), minWidth: 80, }, { - id: 'settings', - label: t('recordingtable.settings'), + id: "settings", + label: t("recordingtable.settings"), minWidth: 80, }, { - id: 'options', - label: t('recordingtable.options'), + id: "options", + label: t("recordingtable.options"), minWidth: 80, }, ]; - const { notify, setRecordings, browserId, setBrowserId, recordingUrl, setRecordingUrl, recordingName, setRecordingName, recordingId, setRecordingId } = useGlobalInfoStore(); + const { + notify, + setRecordings, + browserId, + setBrowserId, + recordingUrl, + setRecordingUrl, + recordingName, + setRecordingName, + recordingId, + setRecordingId, + } = useGlobalInfoStore(); const navigate = useNavigate(); const handleChangePage = (event: unknown, newPage: number) => { setPage(newPage); }; - const handleChangeRowsPerPage = (event: React.ChangeEvent) => { + const handleChangeRowsPerPage = ( + event: React.ChangeEvent + ) => { setRowsPerPage(+event.target.value); setPage(0); }; @@ -103,39 +141,52 @@ export const RecordingsTable = ({ handleEditRecording, handleRunRecording, handl }; const fetchRecordings = async () => { - const recordings = await getStoredRecordings(); - if (recordings) { - const parsedRows: Data[] = []; - recordings.map((recording: any, index: number) => { - if (recording && recording.recording_meta) { - parsedRows.push({ - id: index, - ...recording.recording_meta, - content: recording.recording - }); + try { + const recordings = await getStoredRecordings(); + if (recordings) { + const parsedRows: Data[] = []; + recordings.map((recording: any, index: number) => { + if (recording && recording.recording_meta) { + parsedRows.push({ + id: index, + ...recording.recording_meta, + content: recording.recording, + }); + } + }); + setRecordings(parsedRows.map((recording) => recording.name)); + setRows(parsedRows); + } else { + console.log("No recordings found."); } - }); - setRecordings(parsedRows.map((recording) => recording.name)); - setRows(parsedRows); - } else { - console.log('No recordings found.'); } - } + catch (error) { + notify('error', 'Error fetching recordings.'); + console.error('Error fetching recordings: ', error); + } + }; const handleNewRecording = async () => { if (browserId) { - setBrowserId(null); - await stopRecording(browserId); + try { + await stopRecording(browserId); + } + catch(error) { + notify('error', 'Error stopping recording. Please try again later.'); + console.error('Error stopping recording', error); + } + setBrowserId(null); + } setModalOpen(true); }; const handleStartRecording = () => { - setBrowserId('new-recording'); - setRecordingName(''); - setRecordingId(''); - navigate('/recording'); - } + setBrowserId("new-recording"); + setRecordingName(""); + setRecordingId(""); + navigate("/recording"); + }; const startRecording = () => { setModalOpen(false); @@ -148,54 +199,57 @@ export const RecordingsTable = ({ handleEditRecording, handleRunRecording, handl } }, []); - // Filter rows based on search term const filteredRows = rows.filter((row) => row.name.toLowerCase().includes(searchTerm.toLowerCase()) ); - return ( - {t('recordingtable.heading')} + {t("recordingtable.heading")} + startAdornment: ( + + ), }} - sx={{ width: '250px' }} + sx={{ width: "250px" }} /> - {t('recordingtable.new')} + {t("recordingtable.new")} - + @@ -211,81 +265,149 @@ export const RecordingsTable = ({ handleEditRecording, handleRunRecording, handl - {filteredRows.length !== 0 ? filteredRows - .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) - .map((row) => { - return ( - - {columns.map((column) => { - // @ts-ignore - const value: any = row[column.id]; - if (value !== undefined) { - return ( - - {value} - - ); - } else { - switch (column.id) { - case 'interpret': - return ( - - handleRunRecording(row.id, row.name, row.params || [])} /> - - ); - case 'schedule': - return ( - - handleScheduleRecording(row.id, row.name, row.params || [])} /> - - ); - case 'integrate': + {filteredRows.length !== 0 + ? filteredRows + .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) + .map((row) => { + return ( + + {columns.map((column) => { + // @ts-ignore + const value: any = row[column.id]; + if (value !== undefined) { return ( - handleIntegrateRecording(row.id, row.name, row.params || [])} /> + {value} ); - case 'options': - return ( - - handleEditRobot(row.id, row.name, row.params || [])} - handleDuplicate={() => { - handleDuplicateRobot(row.id, row.name, row.params || []); - }} - handleDelete={() => { - - checkRunsForRecording(row.id).then((result: boolean) => { - if (result) { - notify('warning', t('recordingtable.notifications.delete_warning')); + } else { + switch (column.id) { + case "interpret": + return ( + + + handleRunRecording( + row.id, + row.name, + row.params || [] + ) } - }) - - deleteRecordingFromStorage(row.id).then((result: boolean) => { - if (result) { - setRows([]); - notify('success', t('recordingtable.notifications.delete_success')); - fetchRecordings(); + /> + + ); + case "schedule": + return ( + + + handleScheduleRecording( + row.id, + row.name, + row.params || [] + ) } - }) - }} - /> - - ); - case 'settings': - return ( - - handleSettingsRecording(row.id, row.name, row.params || [])} /> - - ); - default: - return null; - } - } - })} - - ); - }) + /> + + ); + case "integrate": + return ( + + + handleIntegrateRecording( + row.id, + row.name, + row.params || [] + ) + } + /> + + ); + case "options": + return ( + + + handleEditRobot( + row.id, + row.name, + row.params || [] + ) + } + handleDuplicate={() => { + handleDuplicateRobot( + row.id, + row.name, + row.params || [] + ); + }} + handleDelete={() => { + deleteRecordingFromStorage(row.id).then( + (result: boolean) => { + if (result) { + setRows([]); + notify( + "success", + t( + "recordingtable.notifications.delete_success" + ) + ); + fetchRecordings(); + } + } + ).catch(error => { + notify("error", "Error deleting recording."); + console.error("Error deleting recording", error); + }); + }} + + /> + + ); + case "settings": + return ( + + + handleSettingsRecording( + row.id, + row.name, + row.params || [] + ) + } + /> + + ); + default: + return null; + } + } + })} + + ); + }) : null}
@@ -299,16 +421,23 @@ export const RecordingsTable = ({ handleEditRecording, handleRunRecording, handl onPageChange={handleChangePage} onRowsPerPageChange={handleChangeRowsPerPage} /> - setModalOpen(false)} modalStyle={modalStyle}> -
- {t('recordingtable.modal.title')} + setModalOpen(false)} + modalStyle={modalStyle} + + > +
+ + {t("recordingtable.modal.title")} + setRecordingUrl(e.target.value)} - style={{ marginBottom: '20px', marginTop: '20px' }} + style={{ marginBottom: "20px", marginTop: "20px" }} />
); -} +}; interface InterpretButtonProps { handleInterpret: () => void; @@ -330,14 +459,17 @@ interface InterpretButtonProps { const InterpretButton = ({ handleInterpret }: InterpretButtonProps) => { return ( - { - handleInterpret(); - }} + { + handleInterpret(); + }} > - ) -} + ); +}; interface ScheduleButtonProps { handleSchedule: () => void; @@ -345,14 +477,17 @@ interface ScheduleButtonProps { const ScheduleButton = ({ handleSchedule }: ScheduleButtonProps) => { return ( - { - handleSchedule(); - }} + { + handleSchedule(); + }} > - ) -} + ); +}; interface IntegrateButtonProps { handleIntegrate: () => void; @@ -360,14 +495,17 @@ interface IntegrateButtonProps { const IntegrateButton = ({ handleIntegrate }: IntegrateButtonProps) => { return ( - { - handleIntegrate(); - }} + { + handleIntegrate(); + }} > - ) -} + ); +}; interface SettingsButtonProps { handleSettings: () => void; @@ -375,23 +513,36 @@ interface SettingsButtonProps { const SettingsButton = ({ handleSettings }: SettingsButtonProps) => { return ( - { - handleSettings(); - }} + { + handleSettings(); + }} > - ) -} + ); +}; interface OptionsButtonProps { handleEdit: () => void; handleDelete: () => void; handleDuplicate: () => void; + recordingName: string; } -const OptionsButton = ({ handleEdit, handleDelete, handleDuplicate }: OptionsButtonProps) => { +const OptionsButton = ({ + handleEdit, + handleDelete, + handleDuplicate, + recordingName, +}: OptionsButtonProps) => { + const { t } = useTranslation(); const [anchorEl, setAnchorEl] = React.useState(null); + const [deleteDialogOpen, setDeleteDialogOpen] = React.useState(false); + const [deleteModalOpen, setDeleteModalOpen] = React.useState(false); + const { notify } = useGlobalInfoStore(); const handleClick = (event: React.MouseEvent) => { setAnchorEl(event.currentTarget); @@ -401,55 +552,127 @@ const OptionsButton = ({ handleEdit, handleDelete, handleDuplicate }: OptionsBut setAnchorEl(null); }; - const { t } = useTranslation(); + const handleDeleteClick = () => { + setDeleteModalOpen(true); + handleClose(); + }; + + const handleDeleteConfirm = async () => { + try { + checkRunsForRecording(recordingName).then((result: boolean) => { + if (result) { + notify('warning', t('recordingtable.notifications.delete_warning')); + setDeleteModalOpen(false); + } + }) + + + + + // Only proceed with deletion if there are no runs + handleDelete(); + setDeleteModalOpen(false); + notify("success", t("recordingtable.notifications.delete_success")); + } catch (error) { + console.error('Error during deletion:', error); + notify("error", t("recordingtable.notifications.delete_error")); + setDeleteModalOpen(false); + } + }; + + const handleDeleteCancel = () => { + setDeleteDialogOpen(false); + }; + return ( <> - - - { handleEdit(); handleClose(); }}> + { + handleEdit(); + handleClose(); + }} + > - {t('recordingtable.edit')} + {t("recordingtable.edit")} - { handleDelete(); handleClose(); }}> + handleDeleteClick()}> - {t('recordingtable.delete')} + {t("recordingtable.delete")} - { handleDuplicate(); handleClose(); }}> + { + handleDuplicate(); + handleClose(); + }} + > - {t('recordingtable.duplicate')} + {t("recordingtable.duplicate")} + + setDeleteModalOpen(false)} + modalStyle={modalStyle} + > +
+ + {t('recordingtable.delete_confirmation.title')} + + + {t('recordingtable.delete_confirmation.message', { name: recordingName })} + + + + + +
+
); }; const modalStyle = { - top: '50%', - left: '50%', - transform: 'translate(-50%, -50%)', - width: '30%', - backgroundColor: 'background.paper', + top: "50%", + left: "50%", + transform: "translate(-50%, -50%)", + width: "30%", + backgroundColor: "background.paper", p: 4, - height: 'fit-content', - display: 'block', - padding: '20px', + height: "fit-content", + display: "block", + padding: "20px", }; \ No newline at end of file diff --git a/src/components/run/RunsTable.tsx b/src/components/run/RunsTable.tsx index b652aef0f..8b5c89891 100644 --- a/src/components/run/RunsTable.tsx +++ b/src/components/run/RunsTable.tsx @@ -9,6 +9,12 @@ import TableContainer from '@mui/material/TableContainer'; import TableHead from '@mui/material/TableHead'; import TablePagination from '@mui/material/TablePagination'; import TableRow from '@mui/material/TableRow'; +import Dialog from '@mui/material/Dialog'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogContentText from '@mui/material/DialogContentText'; +import DialogTitle from '@mui/material/DialogTitle'; +import Button from '@mui/material/Button'; import { Accordion, AccordionSummary, AccordionDetails, Typography, Box, TextField } from '@mui/material'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import SearchIcon from '@mui/icons-material/Search'; @@ -80,6 +86,8 @@ export const RunsTable: React.FC = ({ const [rowsPerPage, setRowsPerPage] = useState(10); const [rows, setRows] = useState([]); const [searchTerm, setSearchTerm] = useState(''); + const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); + const [runToDelete, setRunToDelete] = useState(null); const { notify, rerenderRuns, setRerenderRuns } = useGlobalInfoStore(); @@ -125,10 +133,22 @@ export const RunsTable: React.FC = ({ } }, [rerenderRuns, rows.length, setRerenderRuns]); - const handleDelete = () => { + const handleDeleteClick = (row: Data) => { + setRunToDelete(row); + setDeleteDialogOpen(true); + }; + + const handleDeleteConfirm = () => { setRows([]); notify('success', t('runstable.notifications.delete_success')); fetchRuns(); + setDeleteDialogOpen(false); + setRunToDelete(null); + }; + + const handleDeleteCancel = () => { + setDeleteDialogOpen(false); + setRunToDelete(null); }; // Filter rows based on search term @@ -190,7 +210,7 @@ export const RunsTable: React.FC = ({ .map((row) => ( handleDeleteClick(row)} key={`row-${row.id}`} isOpen={runId === row.runId && runningRecordingName === row.name} currentLog={currentInterpretationLog} @@ -213,6 +233,30 @@ export const RunsTable: React.FC = ({ onPageChange={handleChangePage} onRowsPerPageChange={handleChangeRowsPerPage} /> + + + + Delete Run + + + + Are you sure you want to delete the run "{runToDelete?.name}"? This action cannot be undone. + + + + + + + ); }; \ No newline at end of file