From c5bc3e9f80eef3fc2637fddba5f98a569a8459df Mon Sep 17 00:00:00 2001 From: AmitChauhan63390 Date: Fri, 2 May 2025 01:21:14 +0530 Subject: [PATCH 01/10] Optional description added to database --- server/src/db/config/database.js | 5 ++-- ...20250501194640-add-description-to-robot.js | 14 ++++++++++ server/src/models/Robot.ts | 28 +++++++++++-------- src/components/recorder/SaveRecording.tsx | 10 +++++++ 4 files changed, 43 insertions(+), 14 deletions(-) create mode 100644 server/src/db/migrations/20250501194640-add-description-to-robot.js diff --git a/server/src/db/config/database.js b/server/src/db/config/database.js index ae6972d14..436bdfac8 100644 --- a/server/src/db/config/database.js +++ b/server/src/db/config/database.js @@ -1,5 +1,5 @@ -import dotenv from 'dotenv'; -dotenv.config({ path: './.env' }); +// CommonJS format for database.js +require('dotenv').config({ path: './.env' }); // Validate required environment variables const requiredEnvVars = ['DB_USER', 'DB_PASSWORD', 'DB_NAME', 'DB_HOST', 'DB_PORT']; @@ -10,7 +10,6 @@ requiredEnvVars.forEach(envVar => { } }); - module.exports = { development: { username: process.env.DB_USER, diff --git a/server/src/db/migrations/20250501194640-add-description-to-robot.js b/server/src/db/migrations/20250501194640-add-description-to-robot.js new file mode 100644 index 000000000..bbe634d4e --- /dev/null +++ b/server/src/db/migrations/20250501194640-add-description-to-robot.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = { + up: async (queryInterface, Sequelize) => { + await queryInterface.addColumn('robot', 'description', { + type: Sequelize.TEXT, + allowNull: true + }); + }, + + down: async (queryInterface, Sequelize) => { + await queryInterface.removeColumn('robot', 'description'); + } +}; \ No newline at end of file diff --git a/server/src/models/Robot.ts b/server/src/models/Robot.ts index 1681eaac8..64385cb03 100644 --- a/server/src/models/Robot.ts +++ b/server/src/models/Robot.ts @@ -18,6 +18,7 @@ interface RobotWorkflow { interface RobotAttributes { id: string; userId?: number; + description?: string | null; recording_meta: RobotMeta; recording: RobotWorkflow; google_sheet_email?: string | null; @@ -25,11 +26,11 @@ interface RobotAttributes { google_sheet_id?: string | null; google_access_token?: string | null; google_refresh_token?: string | null; - airtable_base_id?: string | null; - airtable_base_name?: string | null; - airtable_table_name?: string | null; - airtable_access_token?: string | null; - airtable_refresh_token?: string | null; + airtable_base_id?: string | null; + airtable_base_name?: string | null; + airtable_table_name?: string | null; + airtable_access_token?: string | null; + airtable_refresh_token?: string | null; schedule?: ScheduleConfig | null; airtable_table_id?: string | null; } @@ -52,6 +53,7 @@ interface RobotCreationAttributes extends Optional { } class Robot extends Model implements RobotAttributes { public id!: string; public userId!: number; + public description!: string | null; public recording_meta!: RobotMeta; public recording!: RobotWorkflow; public google_sheet_email!: string | null; @@ -59,12 +61,12 @@ class Robot extends Model implements R public google_sheet_id!: string | null; public google_access_token!: string | null; public google_refresh_token!: string | null; - public airtable_base_id!: string | null; - public airtable_base_name!: string | null; - public airtable_table_name!: string | null; - public airtable_access_token!: string | null; - public airtable_refresh_token!: string | null; - public airtable_table_id!: string | null; + public airtable_base_id!: string | null; + public airtable_base_name!: string | null; + public airtable_table_name!: string | null; + public airtable_access_token!: string | null; + public airtable_refresh_token!: string | null; + public airtable_table_id!: string | null; public schedule!: ScheduleConfig | null; } @@ -79,6 +81,10 @@ Robot.init( type: DataTypes.INTEGER, allowNull: false, }, + description: { + type: DataTypes.TEXT, + allowNull: true, + }, recording_meta: { type: DataTypes.JSONB, allowNull: false, diff --git a/src/components/recorder/SaveRecording.tsx b/src/components/recorder/SaveRecording.tsx index a85cb868f..d75e62c03 100644 --- a/src/components/recorder/SaveRecording.tsx +++ b/src/components/recorder/SaveRecording.tsx @@ -20,6 +20,7 @@ export const SaveRecording = ({ fileName }: SaveRecordingProps) => { const [openModal, setOpenModal] = useState(false); const [needConfirm, setNeedConfirm] = useState(false); const [saveRecordingName, setSaveRecordingName] = useState(fileName); + const [saveRecordingDescription, setSaveRecordingDescription] = useState(""); const [waitingForSave, setWaitingForSave] = useState(false); const { browserId, setBrowserId, notify, recordings, isLogin, recordingName, retrainRobotId } = useGlobalInfoStore(); @@ -153,6 +154,15 @@ export const SaveRecording = ({ fileName }: SaveRecordingProps) => { variant="outlined" value={saveRecordingName} /> + {needConfirm ? ( From f79ed11431c29566059e378e5fafc12ae3a88391 Mon Sep 17 00:00:00 2001 From: AmitChauhan63390 Date: Fri, 2 May 2025 13:26:09 +0530 Subject: [PATCH 02/10] saving description in db --- .../workflow-management/classes/Generator.ts | 12 +++++++----- src/components/recorder/SaveRecording.tsx | 19 ++++++++++++++----- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/server/src/workflow-management/classes/Generator.ts b/server/src/workflow-management/classes/Generator.ts index 1be328aaa..8e701b2a9 100644 --- a/server/src/workflow-management/classes/Generator.ts +++ b/server/src/workflow-management/classes/Generator.ts @@ -141,9 +141,9 @@ export class WorkflowGenerator { */ private registerEventHandlers = (socket: Socket) => { socket.on('save', (data) => { - const { fileName, userId, isLogin, robotId } = data; + const { fileName, userId, isLogin, robotId,description } = data; logger.log('debug', `Saving workflow ${fileName} for user ID ${userId}`); - this.saveNewWorkflow(fileName, userId, isLogin, robotId); + this.saveNewWorkflow(fileName, userId, isLogin, robotId,description); }); socket.on('new-recording', (data) => { this.workflowRecord = { @@ -767,7 +767,7 @@ export class WorkflowGenerator { * @param fileName The name of the file. * @returns {Promise} */ - public saveNewWorkflow = async (fileName: string, userId: number, isLogin: boolean, robotId?: string) => { + public saveNewWorkflow = async (fileName: string, userId: number, isLogin: boolean, robotId?: string,description?: string) => { const recording = this.optimizeWorkflow(this.workflowRecord); let actionType = 'saved'; @@ -784,10 +784,11 @@ export class WorkflowGenerator { params: this.getParams() || [], updatedAt: new Date().toLocaleString(), }, + description: description, }) actionType = 'retrained'; - logger.log('info', `Robot retrained with id: ${robot.id}`); + logger.log('info', `Robot retrained with id: ${robot.id} and name: ${robot.description}`); } } else { this.recordingMeta = { @@ -803,6 +804,7 @@ export class WorkflowGenerator { userId, recording_meta: this.recordingMeta, recording: recording, + description: description, }); capture( 'maxun-oss-robot-created', @@ -813,7 +815,7 @@ export class WorkflowGenerator { ) actionType = 'saved'; - logger.log('info', `Robot saved with id: ${robot.id}`); + logger.log('info', `Robot saved with id: ${robot.id} with description: ${robot.description}`); } } catch (e) { diff --git a/src/components/recorder/SaveRecording.tsx b/src/components/recorder/SaveRecording.tsx index d75e62c03..732d600fb 100644 --- a/src/components/recorder/SaveRecording.tsx +++ b/src/components/recorder/SaveRecording.tsx @@ -43,6 +43,11 @@ export const SaveRecording = ({ fileName }: SaveRecordingProps) => { setSaveRecordingName(value); } + const handleChangeOfDescription = (event: React.ChangeEvent) => { + const { value } = event.target; + setSaveRecordingDescription(value); + } + const handleSaveRecording = async (event: React.SyntheticEvent) => { event.preventDefault(); if (recordings.includes(saveRecordingName)) { @@ -109,10 +114,13 @@ export const SaveRecording = ({ fileName }: SaveRecordingProps) => { userId: user.id, isLogin: isLogin, robotId: retrainRobotId, + description: saveRecordingDescription, }; + + console.log(payload) socket?.emit('save', payload); setWaitingForSave(true); - console.log(`Saving the recording as ${saveRecordingName || recordingName} for userId ${user.id}`); + console.log(`Saving the recording as ${saveRecordingName || recordingName} for userId ${user.id} with description: ${saveRecordingDescription}`); } else { console.error(t('save_recording.notifications.user_not_logged')); } @@ -155,13 +163,14 @@ export const SaveRecording = ({ fileName }: SaveRecordingProps) => { value={saveRecordingName} /> {needConfirm ? From 3b85046450eed17dea6a8ef5a31570a65bb5c7c2 Mon Sep 17 00:00:00 2001 From: AmitChauhan63390 Date: Sat, 3 May 2025 00:12:25 +0530 Subject: [PATCH 03/10] description display and updation --- server/src/routes/storage.ts | 6 +- src/api/storage.ts | 2 +- src/components/recorder/SaveRecording.tsx | 252 +++-- src/components/robot/RobotEdit.tsx | 1009 +++++++++++---------- src/components/robot/RobotSettings.tsx | 13 + 5 files changed, 728 insertions(+), 554 deletions(-) diff --git a/server/src/routes/storage.ts b/server/src/routes/storage.ts index e7e3939c0..f8145f387 100644 --- a/server/src/routes/storage.ts +++ b/server/src/routes/storage.ts @@ -254,7 +254,7 @@ function handleWorkflowActions(workflow: any[], credentials: Credentials) { router.put('/recordings/:id', requireSignIn, async (req: AuthenticatedRequest, res) => { try { const { id } = req.params; - const { name, limit, credentials, targetUrl } = req.body; + const { name, limit, credentials, targetUrl,description } = req.body; // Validate input if (!name && limit === undefined && !targetUrl) { @@ -288,6 +288,10 @@ router.put('/recordings/:id', requireSignIn, async (req: AuthenticatedRequest, r return step; }); + if(description){ + robot.set('description', description); + } + robot.set('recording', { ...robot.recording, workflow: updatedWorkflow }); robot.changed('recording', true); } diff --git a/src/api/storage.ts b/src/api/storage.ts index e3359563c..0f8127d49 100644 --- a/src/api/storage.ts +++ b/src/api/storage.ts @@ -28,7 +28,7 @@ export const getStoredRecordings = async (): Promise => { } }; -export const updateRecording = async (id: string, data: { name?: string; limit?: number, credentials?: Credentials, targetUrl?: string }): Promise => { +export const updateRecording = async (id: string, data: { name?: string; limit?: number, credentials?: Credentials, targetUrl?: string,description?:string }): Promise => { try { const response = await axios.put(`${apiUrl}/storage/recordings/${id}`, data); if (response.status === 200) { diff --git a/src/components/recorder/SaveRecording.tsx b/src/components/recorder/SaveRecording.tsx index 732d600fb..791f51848 100644 --- a/src/components/recorder/SaveRecording.tsx +++ b/src/components/recorder/SaveRecording.tsx @@ -1,15 +1,15 @@ -import React, { useCallback, useEffect, useState, useContext } from 'react'; +import React, { useCallback, useEffect, useState, useContext } from "react"; import { Button, Box, LinearProgress, Tooltip } from "@mui/material"; import { GenericModal } from "../ui/GenericModal"; import { stopRecording } from "../../api/recording"; import { useGlobalInfoStore } from "../../context/globalInfo"; -import { AuthContext } from '../../context/auth'; +import { AuthContext } from "../../context/auth"; import { useSocketStore } from "../../context/socket"; import { TextField, Typography } from "@mui/material"; import { WarningText } from "../ui/texts"; import NotificationImportantIcon from "@mui/icons-material/NotificationImportant"; -import { useNavigate } from 'react-router-dom'; -import { useTranslation } from 'react-i18next'; +import { useNavigate } from "react-router-dom"; +import { useTranslation } from "react-i18next"; interface SaveRecordingProps { fileName: string; @@ -20,10 +20,19 @@ export const SaveRecording = ({ fileName }: SaveRecordingProps) => { const [openModal, setOpenModal] = useState(false); const [needConfirm, setNeedConfirm] = useState(false); const [saveRecordingName, setSaveRecordingName] = useState(fileName); - const [saveRecordingDescription, setSaveRecordingDescription] = useState(""); + const [saveRecordingDescription, setSaveRecordingDescription] = + useState(""); const [waitingForSave, setWaitingForSave] = useState(false); - const { browserId, setBrowserId, notify, recordings, isLogin, recordingName, retrainRobotId } = useGlobalInfoStore(); + const { + browserId, + setBrowserId, + notify, + recordings, + isLogin, + recordingName, + retrainRobotId, + } = useGlobalInfoStore(); const { socket } = useSocketStore(); const { state, dispatch } = useContext(AuthContext); const { user } = state; @@ -41,17 +50,21 @@ export const SaveRecording = ({ fileName }: SaveRecordingProps) => { setNeedConfirm(false); } setSaveRecordingName(value); - } + }; - const handleChangeOfDescription = (event: React.ChangeEvent) => { + const handleChangeOfDescription = ( + event: React.ChangeEvent + ) => { const { value } = event.target; setSaveRecordingDescription(value); - } + }; const handleSaveRecording = async (event: React.SyntheticEvent) => { event.preventDefault(); if (recordings.includes(saveRecordingName)) { - if (needConfirm) { return; } + if (needConfirm) { + return; + } setNeedConfirm(true); } else { await saveRecording(); @@ -66,71 +79,84 @@ 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'); - } else if (data.actionType === 'saved') { - successMessage = t('save_recording.notifications.save_success'); - } else if (data.actionType === 'error') { - successMessage = t('save_recording.notifications.save_error'); + 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"); + } else if (data.actionType === "saved") { + successMessage = t("save_recording.notifications.save_success"); + } else if (data.actionType === "error") { + successMessage = t("save_recording.notifications.save_error"); + } } - } - - const notificationData = { - type: 'success', - message: successMessage, - timestamp: Date.now() - }; - - 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]); + + const notificationData = { + type: "success", + message: successMessage, + timestamp: Date.now(), + }; + + 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] + ); // notifies backed to save the recording in progress, // releases resources and changes the view for main page by clearing the global browserId const saveRecording = async () => { if (user) { - const payload = { - fileName: saveRecordingName || recordingName, - userId: user.id, + const payload = { + fileName: saveRecordingName || recordingName, + userId: user.id, isLogin: isLogin, robotId: retrainRobotId, description: saveRecordingDescription, }; - console.log(payload) - socket?.emit('save', payload); + console.log(payload); + socket?.emit("save", payload); setWaitingForSave(true); - console.log(`Saving the recording as ${saveRecordingName || recordingName} for userId ${user.id} with description: ${saveRecordingDescription}`); + console.log( + `Saving the recording as ${ + saveRecordingName || recordingName + } for userId ${user.id} with description: ${saveRecordingDescription}` + ); } else { - console.error(t('save_recording.notifications.user_not_logged')); + console.error(t("save_recording.notifications.user_not_logged")); } }; useEffect(() => { - socket?.on('fileSaved', exitRecording); + socket?.on("fileSaved", exitRecording); return () => { - socket?.off('fileSaved', exitRecording); - } + socket?.off("fileSaved", exitRecording); + }; }, [socket, exitRecording]); return ( @@ -140,74 +166,112 @@ export const SaveRecording = ({ fileName }: SaveRecordingProps) => { variant="outlined" color="success" sx={{ - marginRight: '20px', - color: '#00c853 !important', - borderColor: '#00c853 !important', - backgroundColor: 'whitesmoke !important', + marginRight: "20px", + color: "#00c853 !important", + borderColor: "#00c853 !important", + backgroundColor: "whitesmoke !important", }} size="small" > - {t('right_panel.buttons.finish')} + {t("right_panel.buttons.finish")} - setOpenModal(false)} modalStyle={modalStyle}> -
- {t('save_recording.title')} + setOpenModal(false)} + modalStyle={modalStyle} + > + + {t("save_recording.title")} + setSaveRecordingDescription(e.target.value.slice(0, 50)) + } id="description" - label={t('save_recording.description')} + label={t("save_recording.description")} variant="outlined" value={saveRecordingDescription} multiline rows={2} + inputProps={{ maxLength: 50 }} + helperText={ + + {50 - saveRecordingDescription.length} characters remaining + + } /> - {needConfirm - ? - ( - - {t('save_recording.errors.exists_warning')} + {t("save_recording.errors.exists_warning")} - ) - : - } - {waitingForSave && - - + )} + {waitingForSave && ( + + - } + )}
); -} +}; const modalStyle = { - top: '25%', - left: '50%', - transform: 'translate(-50%, -50%)', - width: '30%', - backgroundColor: 'background.paper', + top: "25%", + left: "50%", + transform: "translate(-50%, -50%)", + width: "30%", + backgroundColor: "background.paper", p: 4, - height: 'fit-content', - display: 'block', - padding: '20px', -}; \ No newline at end of file + height: "fit-content", + display: "block", + padding: "20px", +}; diff --git a/src/components/robot/RobotEdit.tsx b/src/components/robot/RobotEdit.tsx index 077c97ccd..aa227e6ee 100644 --- a/src/components/robot/RobotEdit.tsx +++ b/src/components/robot/RobotEdit.tsx @@ -1,519 +1,612 @@ -import React, { useState, useEffect } from 'react'; -import { useTranslation } from 'react-i18next'; +import React, { useState, useEffect } from "react"; +import { useTranslation } from "react-i18next"; import { GenericModal } from "../ui/GenericModal"; -import { TextField, Typography, Box, Button, IconButton, InputAdornment } from "@mui/material"; -import { Visibility, VisibilityOff } from '@mui/icons-material'; +import { + TextField, + Typography, + Box, + Button, + IconButton, + InputAdornment, +} from "@mui/material"; +import { Visibility, VisibilityOff } from "@mui/icons-material"; import { modalStyle } from "../recorder/AddWhereCondModal"; -import { useGlobalInfoStore } from '../../context/globalInfo'; -import { getStoredRecording, updateRecording } from '../../api/storage'; -import { WhereWhatPair } from 'maxun-core'; +import { useGlobalInfoStore } from "../../context/globalInfo"; +import { getStoredRecording, updateRecording } from "../../api/storage"; +import { WhereWhatPair } from "maxun-core"; interface RobotMeta { - name: string; - id: string; - createdAt: string; - pairs: number; - updatedAt: string; - params: any[]; + name: string; + id: string; + createdAt: string; + pairs: number; + updatedAt: string; + params: any[]; } interface RobotWorkflow { - workflow: WhereWhatPair[]; + workflow: WhereWhatPair[]; } interface ScheduleConfig { - runEvery: number; - runEveryUnit: 'MINUTES' | 'HOURS' | 'DAYS' | 'WEEKS' | 'MONTHS'; - startFrom: 'SUNDAY' | 'MONDAY' | 'TUESDAY' | 'WEDNESDAY' | 'THURSDAY' | 'FRIDAY' | 'SATURDAY'; - atTimeStart?: string; - atTimeEnd?: string; - timezone: string; - lastRunAt?: Date; - nextRunAt?: Date; - cronExpression?: string; + runEvery: number; + runEveryUnit: "MINUTES" | "HOURS" | "DAYS" | "WEEKS" | "MONTHS"; + startFrom: + | "SUNDAY" + | "MONDAY" + | "TUESDAY" + | "WEDNESDAY" + | "THURSDAY" + | "FRIDAY" + | "SATURDAY"; + atTimeStart?: string; + atTimeEnd?: string; + timezone: string; + lastRunAt?: Date; + nextRunAt?: Date; + cronExpression?: string; } export interface RobotSettings { - id: string; - userId?: number; - recording_meta: RobotMeta; - recording: RobotWorkflow; - google_sheet_email?: string | null; - google_sheet_name?: string | null; - google_sheet_id?: string | null; - google_access_token?: string | null; - google_refresh_token?: string | null; - schedule?: ScheduleConfig | null; + id: string; + userId?: number; + recording_meta: RobotMeta; + recording: RobotWorkflow; + google_sheet_email?: string | null; + google_sheet_name?: string | null; + google_sheet_id?: string | null; + google_access_token?: string | null; + google_refresh_token?: string | null; + schedule?: ScheduleConfig | null; + description?: string; } interface RobotSettingsProps { - isOpen: boolean; - handleStart: (settings: RobotSettings) => void; - handleClose: () => void; - initialSettings?: RobotSettings | null; + isOpen: boolean; + handleStart: (settings: RobotSettings) => void; + handleClose: () => void; + initialSettings?: RobotSettings | null; } interface CredentialInfo { - value: string; - type: string; + value: string; + type: string; } interface Credentials { - [key: string]: CredentialInfo; + [key: string]: CredentialInfo; } interface CredentialVisibility { - [key: string]: boolean; + [key: string]: boolean; } interface GroupedCredentials { - passwords: string[]; - emails: string[]; - usernames: string[]; - others: string[]; + passwords: string[]; + emails: string[]; + usernames: string[]; + others: string[]; } -export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettings }: RobotSettingsProps) => { - const { t } = useTranslation(); - const [credentials, setCredentials] = useState({}); - const { recordingId, notify, setRerenderRobots } = useGlobalInfoStore(); - const [robot, setRobot] = useState(null); - const [credentialGroups, setCredentialGroups] = useState({ - passwords: [], - emails: [], - usernames: [], - others: [] - }); - const [showPasswords, setShowPasswords] = useState({}); +export const RobotEditModal = ({ + isOpen, + handleStart, + handleClose, + initialSettings, +}: RobotSettingsProps) => { + const { t } = useTranslation(); + const [credentials, setCredentials] = useState({}); + const { recordingId, notify, setRerenderRobots } = useGlobalInfoStore(); + const [robot, setRobot] = useState(null); + const [credentialGroups, setCredentialGroups] = useState({ + passwords: [], + emails: [], + usernames: [], + others: [], + }); + const [showPasswords, setShowPasswords] = useState({}); + + const isEmailPattern = (value: string): boolean => { + return value.includes("@"); + }; + + const isUsernameSelector = (selector: string): boolean => { + return ( + selector.toLowerCase().includes("username") || + selector.toLowerCase().includes("user") || + selector.toLowerCase().includes("email") + ); + }; + + const determineCredentialType = ( + selector: string, + info: CredentialInfo + ): "password" | "email" | "username" | "other" => { + if ( + info.type === "password" || + selector.toLowerCase().includes("password") + ) { + return "password"; + } + if ( + isEmailPattern(info.value) || + selector.toLowerCase().includes("email") + ) { + return "email"; + } + if (isUsernameSelector(selector)) { + return "username"; + } + return "other"; + }; - const isEmailPattern = (value: string): boolean => { - return value.includes('@'); - }; + useEffect(() => { + if (isOpen) { + getRobot(); + } + }, [isOpen]); + + useEffect(() => { + if (robot?.recording?.workflow) { + const extractedCredentials = extractInitialCredentials( + robot.recording.workflow + ); + setCredentials(extractedCredentials); + setCredentialGroups(groupCredentialsByType(extractedCredentials)); + } + }, [robot]); - const isUsernameSelector = (selector: string): boolean => { - return selector.toLowerCase().includes('username') || - selector.toLowerCase().includes('user') || - selector.toLowerCase().includes('email'); - }; + function extractInitialCredentials(workflow: any[]): Credentials { + const credentials: Credentials = {}; - const determineCredentialType = (selector: string, info: CredentialInfo): 'password' | 'email' | 'username' | 'other' => { - if (info.type === 'password' || selector.toLowerCase().includes('password')) { - return 'password'; - } - if (isEmailPattern(info.value) || selector.toLowerCase().includes('email')) { - return 'email'; - } - if (isUsernameSelector(selector)) { - return 'username'; - } - return 'other'; + const isPrintableCharacter = (char: string): boolean => { + return char.length === 1 && !!char.match(/^[\x20-\x7E]$/); }; - useEffect(() => { - if (isOpen) { - getRobot(); - } - }, [isOpen]); - - useEffect(() => { - if (robot?.recording?.workflow) { - const extractedCredentials = extractInitialCredentials(robot.recording.workflow); - setCredentials(extractedCredentials); - setCredentialGroups(groupCredentialsByType(extractedCredentials)); - } - }, [robot]); - - function extractInitialCredentials(workflow: any[]): Credentials { - const credentials: Credentials = {}; - - const isPrintableCharacter = (char: string): boolean => { - return char.length === 1 && !!char.match(/^[\x20-\x7E]$/); - }; - - workflow.forEach(step => { - if (!step.what) return; - - let currentSelector = ''; - let currentValue = ''; - let currentType = ''; - let i = 0; - - while (i < step.what.length) { - const action = step.what[i]; - - if (!action.action || !action.args?.[0]) { - i++; - continue; - } - - const selector = action.args[0]; - - // Handle full word type actions first - if (action.action === 'type' && - action.args?.length >= 2 && - typeof action.args[1] === 'string' && - action.args[1].length > 1) { - - if (!credentials[selector]) { - credentials[selector] = { - value: action.args[1], - type: action.args[2] || 'text' - }; - } - i++; - continue; - } - - // Handle character-by-character sequences (both type and press) - if ((action.action === 'type' || action.action === 'press') && - action.args?.length >= 2 && - typeof action.args[1] === 'string') { - - if (selector !== currentSelector) { - if (currentSelector && currentValue) { - credentials[currentSelector] = { - value: currentValue, - type: currentType || 'text' - }; - } - currentSelector = selector; - currentValue = credentials[selector]?.value || ''; - currentType = action.args[2] || credentials[selector]?.type || 'text'; - } - - const character = action.args[1]; - - if (isPrintableCharacter(character)) { - currentValue += character; - } else if (character === 'Backspace') { - currentValue = currentValue.slice(0, -1); - } + workflow.forEach((step) => { + if (!step.what) return; - if (!currentType && action.args[2]?.toLowerCase() === 'password') { - currentType = 'password'; - } - - let j = i + 1; - while (j < step.what.length) { - const nextAction = step.what[j]; - if (!nextAction.action || !nextAction.args?.[0] || - nextAction.args[0] !== selector || - (nextAction.action !== 'type' && nextAction.action !== 'press')) { - break; - } - if (nextAction.args[1] === 'Backspace') { - currentValue = currentValue.slice(0, -1); - } else if (isPrintableCharacter(nextAction.args[1])) { - currentValue += nextAction.args[1]; - } - j++; - } + let currentSelector = ""; + let currentValue = ""; + let currentType = ""; + let i = 0; - credentials[currentSelector] = { - value: currentValue, - type: currentType - }; + while (i < step.what.length) { + const action = step.what[i]; - i = j; - } else { - i++; - } - } - - if (currentSelector && currentValue) { - credentials[currentSelector] = { - value: currentValue, - type: currentType || 'text' - }; - } - }); - - return credentials; - } - - const groupCredentialsByType = (credentials: Credentials): GroupedCredentials => { - return Object.entries(credentials).reduce((acc: GroupedCredentials, [selector, info]) => { - const credentialType = determineCredentialType(selector, info); - - switch (credentialType) { - case 'password': - acc.passwords.push(selector); - break; - case 'email': - acc.emails.push(selector); - break; - case 'username': - acc.usernames.push(selector); - break; - default: - acc.others.push(selector); - } - - return acc; - }, { passwords: [], emails: [], usernames: [], others: [] }); - }; - - const getRobot = async () => { - if (recordingId) { - const robot = await getStoredRecording(recordingId); - setRobot(robot); - } else { - notify('error', t('robot_edit.notifications.update_failed')); + if (!action.action || !action.args?.[0]) { + i++; + continue; } - }; - const handleClickShowPassword = (selector: string) => { - setShowPasswords(prev => ({ - ...prev, - [selector]: !prev[selector] - })); - }; - - const handleRobotNameChange = (newName: string) => { - setRobot((prev) => - prev ? { ...prev, recording_meta: { ...prev.recording_meta, name: newName } } : prev - ); - }; + const selector = action.args[0]; + + // Handle full word type actions first + if ( + action.action === "type" && + action.args?.length >= 2 && + typeof action.args[1] === "string" && + action.args[1].length > 1 + ) { + if (!credentials[selector]) { + credentials[selector] = { + value: action.args[1], + type: action.args[2] || "text", + }; + } + i++; + continue; + } - const handleCredentialChange = (selector: string, value: string) => { - setCredentials(prev => ({ - ...prev, - [selector]: { - ...prev[selector], - value + // Handle character-by-character sequences (both type and press) + if ( + (action.action === "type" || action.action === "press") && + action.args?.length >= 2 && + typeof action.args[1] === "string" + ) { + if (selector !== currentSelector) { + if (currentSelector && currentValue) { + credentials[currentSelector] = { + value: currentValue, + type: currentType || "text", + }; } - })); - }; - - const handleLimitChange = (newLimit: number) => { - setRobot((prev) => { - if (!prev) return prev; - - const updatedWorkflow = [...prev.recording.workflow]; + currentSelector = selector; + currentValue = credentials[selector]?.value || ""; + currentType = + action.args[2] || credentials[selector]?.type || "text"; + } + + const character = action.args[1]; + + if (isPrintableCharacter(character)) { + currentValue += character; + } else if (character === "Backspace") { + currentValue = currentValue.slice(0, -1); + } + + if (!currentType && action.args[2]?.toLowerCase() === "password") { + currentType = "password"; + } + + let j = i + 1; + while (j < step.what.length) { + const nextAction = step.what[j]; if ( - updatedWorkflow.length > 0 && - updatedWorkflow[0]?.what && - updatedWorkflow[0].what.length > 0 && - updatedWorkflow[0].what[0].args && - updatedWorkflow[0].what[0].args.length > 0 && - updatedWorkflow[0].what[0].args[0] + !nextAction.action || + !nextAction.args?.[0] || + nextAction.args[0] !== selector || + (nextAction.action !== "type" && nextAction.action !== "press") ) { - updatedWorkflow[0].what[0].args[0].limit = newLimit; + break; } - - return { ...prev, recording: { ...prev.recording, workflow: updatedWorkflow } }; - }); - }; - - const handleTargetUrlChange = (newUrl: string) => { - setRobot((prev) => { - if (!prev) return prev; - - const updatedWorkflow = [...prev.recording.workflow]; - const lastPairIndex = updatedWorkflow.length - 1; - - if (lastPairIndex >= 0) { - const gotoAction = updatedWorkflow[lastPairIndex]?.what?.find(action => action.action === "goto"); - if (gotoAction && gotoAction.args && gotoAction.args.length > 0) { - gotoAction.args[0] = newUrl; - } + if (nextAction.args[1] === "Backspace") { + currentValue = currentValue.slice(0, -1); + } else if (isPrintableCharacter(nextAction.args[1])) { + currentValue += nextAction.args[1]; } + j++; + } - return { ...prev, recording: { ...prev.recording, workflow: updatedWorkflow } }; - }); - }; + credentials[currentSelector] = { + value: currentValue, + type: currentType, + }; - const renderAllCredentialFields = () => { - return ( - <> - {renderCredentialFields( - credentialGroups.usernames, - t('Username'), - 'text' - )} - - {renderCredentialFields( - credentialGroups.emails, - t('Email'), - 'text' - )} - - {renderCredentialFields( - credentialGroups.passwords, - t('Password'), - 'password' - )} - - {renderCredentialFields( - credentialGroups.others, - t('Other'), - 'text' - )} - - ); - }; - - const renderCredentialFields = (selectors: string[], headerText: string, defaultType: 'text' | 'password' = 'text') => { - if (selectors.length === 0) return null; - - return ( - <> - {/* - {headerText} - */} - {selectors.map((selector, index) => { - const isVisible = showPasswords[selector]; - - return ( - handleCredentialChange(selector, e.target.value)} - style={{ marginBottom: '20px' }} - InputProps={{ - endAdornment: ( - - handleClickShowPassword(selector)} - edge="end" - disabled={!credentials[selector]?.value} - > - {isVisible ? : } - - - ), - }} - /> - ); - })} - - ); - }; + i = j; + } else { + i++; + } + } - const handleSave = async () => { - if (!robot) return; + if (currentSelector && currentValue) { + credentials[currentSelector] = { + value: currentValue, + type: currentType || "text", + }; + } + }); - try { - const credentialsForPayload = Object.entries(credentials).reduce((acc, [selector, info]) => { - const enforceType = info.type === 'password' ? 'password' : 'text'; + return credentials; + } + + const groupCredentialsByType = ( + credentials: Credentials + ): GroupedCredentials => { + return Object.entries(credentials).reduce( + (acc: GroupedCredentials, [selector, info]) => { + const credentialType = determineCredentialType(selector, info); + + switch (credentialType) { + case "password": + acc.passwords.push(selector); + break; + case "email": + acc.emails.push(selector); + break; + case "username": + acc.usernames.push(selector); + break; + default: + acc.others.push(selector); + } - acc[selector] = { - value: info.value, - type: enforceType - }; - return acc; - }, {} as Record); + return acc; + }, + { passwords: [], emails: [], usernames: [], others: [] } + ); + }; + + const getRobot = async () => { + if (recordingId) { + const robot = await getStoredRecording(recordingId); + setRobot(robot); + } else { + notify("error", t("robot_edit.notifications.update_failed")); + } + }; + + const handleClickShowPassword = (selector: string) => { + setShowPasswords((prev) => ({ + ...prev, + [selector]: !prev[selector], + })); + }; + + const handleRobotNameChange = (newName: string) => { + setRobot((prev) => + prev + ? { ...prev, recording_meta: { ...prev.recording_meta, name: newName } } + : prev + ); + }; + const handleRobotNameDescription = (newDescription: string) => { + setRobot((prev) => + prev ? { ...prev, description: newDescription } : prev + ); + }; + + const handleCredentialChange = (selector: string, value: string) => { + setCredentials((prev) => ({ + ...prev, + [selector]: { + ...prev[selector], + value, + }, + })); + }; + + const handleLimitChange = (newLimit: number) => { + setRobot((prev) => { + if (!prev) return prev; + + const updatedWorkflow = [...prev.recording.workflow]; + if ( + updatedWorkflow.length > 0 && + updatedWorkflow[0]?.what && + updatedWorkflow[0].what.length > 0 && + updatedWorkflow[0].what[0].args && + updatedWorkflow[0].what[0].args.length > 0 && + updatedWorkflow[0].what[0].args[0] + ) { + updatedWorkflow[0].what[0].args[0].limit = newLimit; + } + + return { + ...prev, + recording: { ...prev.recording, workflow: updatedWorkflow }, + }; + }); + }; - const lastPair = robot.recording.workflow[robot.recording.workflow.length - 1]; - const targetUrl = lastPair?.what.find(action => action.action === "goto")?.args?.[0]; + const handleTargetUrlChange = (newUrl: string) => { + setRobot((prev) => { + if (!prev) return prev; - const payload = { - name: robot.recording_meta.name, - limit: robot.recording.workflow[0]?.what[0]?.args?.[0]?.limit, - credentials: credentialsForPayload, - targetUrl: targetUrl, - }; + const updatedWorkflow = [...prev.recording.workflow]; + const lastPairIndex = updatedWorkflow.length - 1; - const success = await updateRecording(robot.recording_meta.id, payload); + if (lastPairIndex >= 0) { + const gotoAction = updatedWorkflow[lastPairIndex]?.what?.find( + (action) => action.action === "goto" + ); + if (gotoAction && gotoAction.args && gotoAction.args.length > 0) { + gotoAction.args[0] = newUrl; + } + } - if (success) { - setRerenderRobots(true); + return { + ...prev, + recording: { ...prev.recording, workflow: updatedWorkflow }, + }; + }); + }; - notify('success', t('robot_edit.notifications.update_success')); - handleStart(robot); - handleClose(); - } else { - notify('error', t('robot_edit.notifications.update_failed')); - } - } catch (error) { - notify('error', t('robot_edit.notifications.update_error')); - console.error('Error updating robot:', error); - } - }; + const renderAllCredentialFields = () => { + return ( + <> + {renderCredentialFields( + credentialGroups.usernames, + t("Username"), + "text" + )} + + {renderCredentialFields(credentialGroups.emails, t("Email"), "text")} + + {renderCredentialFields( + credentialGroups.passwords, + t("Password"), + "password" + )} + + {renderCredentialFields(credentialGroups.others, t("Other"), "text")} + + ); + }; - const lastPair = robot?.recording.workflow[robot?.recording.workflow.length - 1]; - const targetUrl = lastPair?.what.find(action => action.action === "goto")?.args?.[0]; + const renderCredentialFields = ( + selectors: string[], + headerText: string, + defaultType: "text" | "password" = "text" + ) => { + if (selectors.length === 0) return null; return ( - + <> + {/* + {headerText} + */} + {selectors.map((selector, index) => { + const isVisible = showPasswords[selector]; + + return ( + handleCredentialChange(selector, e.target.value)} + style={{ marginBottom: "20px" }} + InputProps={{ + endAdornment: ( + + handleClickShowPassword(selector)} + edge="end" + disabled={!credentials[selector]?.value} + > + {isVisible ? : } + + + ), + }} + /> + ); + })} + + ); + }; + + const handleSave = async () => { + if (!robot) return; + + try { + const credentialsForPayload = Object.entries(credentials).reduce( + (acc, [selector, info]) => { + const enforceType = info.type === "password" ? "password" : "text"; + + acc[selector] = { + value: info.value, + type: enforceType, + }; + return acc; + }, + {} as Record + ); + + const lastPair = + robot.recording.workflow[robot.recording.workflow.length - 1]; + const targetUrl = lastPair?.what.find( + (action) => action.action === "goto" + )?.args?.[0]; + + const description = robot.description || ""; + const payload = { + name: robot.recording_meta.name, + limit: robot.recording.workflow[0]?.what[0]?.args?.[0]?.limit, + credentials: credentialsForPayload, + targetUrl: targetUrl, + description: description, + }; + + const success = await updateRecording(robot.recording_meta.id, payload); + + if (success) { + setRerenderRobots(true); + + notify("success", t("robot_edit.notifications.update_success")); + handleStart(robot); + handleClose(); + } else { + notify("error", t("robot_edit.notifications.update_failed")); + } + } catch (error) { + notify("error", t("robot_edit.notifications.update_error")); + console.error("Error updating robot:", error); + } + }; + + const lastPair = + robot?.recording.workflow[robot?.recording.workflow.length - 1]; + const targetUrl = lastPair?.what.find((action) => action.action === "goto") + ?.args?.[0]; + + return ( + + <> + + {t("robot_edit.title")} + + + {robot && ( <> - - {t('robot_edit.title')} - - - {robot && ( - <> - handleRobotNameChange(e.target.value)} - style={{ marginBottom: '20px' }} - /> - - handleTargetUrlChange(e.target.value)} - style={{ marginBottom: '20px' }} - /> - - {robot.recording.workflow?.[0]?.what?.[0]?.args?.[0]?.limit !== undefined && ( - { - const value = parseInt(e.target.value, 10); - if (value >= 1) { - handleLimitChange(value); - } - }} - inputProps={{ min: 1 }} - style={{ marginBottom: '20px' }} - /> - )} - - {(Object.keys(credentials).length > 0) && ( - <> - - {t('Input Texts')} - - {renderAllCredentialFields()} - - )} - - - - - - - )} - + handleRobotNameChange(e.target.value)} + style={{ marginBottom: "20px" }} + /> + + handleTargetUrlChange(e.target.value)} + style={{ marginBottom: "20px" }} + /> + + {robot.recording.workflow?.[0]?.what?.[0]?.args?.[0]?.limit !== + undefined && ( + { + const value = parseInt(e.target.value, 10); + if (value >= 1) { + handleLimitChange(value); + } + }} + inputProps={{ min: 1 }} + style={{ marginBottom: "20px" }} + /> + )} + + {robot.description?.length !== undefined && + handleRobotNameDescription(e.target.value.slice(0, 50)) + } + style={{ marginBottom: "20px" }} + inputProps={{ maxLength: 50 }} + helperText={ + + {50 - (robot.description?.length ?? 0)} characters remaining + + } + />} + + {Object.keys(credentials).length > 0 && ( + <> + + {t("Input Texts")} + + {renderAllCredentialFields()} + + )} + + + + + - - ); -}; \ No newline at end of file + )} + + + + ); +}; diff --git a/src/components/robot/RobotSettings.tsx b/src/components/robot/RobotSettings.tsx index 6ae59f89b..e4e57a794 100644 --- a/src/components/robot/RobotSettings.tsx +++ b/src/components/robot/RobotSettings.tsx @@ -14,6 +14,7 @@ interface RobotMeta { pairs: number; updatedAt: string; params: any[]; + } interface RobotWorkflow { @@ -43,6 +44,7 @@ export interface RobotSettings { google_access_token?: string | null; google_refresh_token?: string | null; schedule?: ScheduleConfig | null; + description?: string|null; } interface RobotSettingsProps { @@ -151,6 +153,17 @@ export const RobotSettingsModal = ({ isOpen, handleStart, handleClose, initialSe }} style={{ marginBottom: '20px' }} /> + {robot.description && ( + + )} ) } From fdf1ec5857cc1933d1be526b48695ef713aeab7f Mon Sep 17 00:00:00 2001 From: AmitChauhan63390 Date: Sat, 3 May 2025 02:58:21 +0530 Subject: [PATCH 04/10] ui changes --- src/components/recorder/SaveRecording.tsx | 70 +++++++++++++---------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/src/components/recorder/SaveRecording.tsx b/src/components/recorder/SaveRecording.tsx index 791f51848..883e87ca7 100644 --- a/src/components/recorder/SaveRecording.tsx +++ b/src/components/recorder/SaveRecording.tsx @@ -186,76 +186,85 @@ export const SaveRecording = ({ fileName }: SaveRecordingProps) => { style={{ display: "flex", flexDirection: "column", - alignItems: "flex-start", + gap: "16px", + width: "100%", }} > - {t("save_recording.title")} + + {t("save_recording.title")} + + + - setSaveRecordingDescription(e.target.value.slice(0, 50)) - } + fullWidth id="description" - label={t("save_recording.description")} variant="outlined" + label={t("save_recording.description")} value={saveRecordingDescription} + onChange={(e) => + setSaveRecordingDescription(e.target.value.slice(0, 50)) + } multiline rows={2} inputProps={{ maxLength: 50 }} helperText={ - {50 - saveRecordingDescription.length} characters remaining + {t("common.optional")} + {50 - saveRecordingDescription.length} / 50 } /> + {needConfirm ? ( - +
- + {t("save_recording.errors.exists_warning")} - +
) : ( - )} + {waitingForSave && ( - - - + )} @@ -268,10 +277,11 @@ const modalStyle = { top: "25%", left: "50%", transform: "translate(-50%, -50%)", - width: "30%", - backgroundColor: "background.paper", - p: 4, - height: "fit-content", + width: "400px", + maxWidth: "90vw", + bgcolor: "background.paper", + boxShadow: "24px", + p: 3, + borderRadius: 2, display: "block", - padding: "20px", -}; +}; \ No newline at end of file From cf2af43e5a9958893839e7f6751d5382fa4b3ec7 Mon Sep 17 00:00:00 2001 From: AmitChauhan63390 Date: Fri, 30 May 2025 13:16:32 +0530 Subject: [PATCH 05/10] test --- src/components/recorder/AddWhereCondModal.tsx | 4 ++++ src/components/robot/RobotEdit.tsx | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/recorder/AddWhereCondModal.tsx b/src/components/recorder/AddWhereCondModal.tsx index 7c5c284c8..2ace72516 100644 --- a/src/components/recorder/AddWhereCondModal.tsx +++ b/src/components/recorder/AddWhereCondModal.tsx @@ -139,6 +139,10 @@ export const AddWhereCondModal = ({ isOpen, onClose, pair, index }: AddWhereCond ) } + + + + export const modalStyle = { top: '40%', left: '50%', diff --git a/src/components/robot/RobotEdit.tsx b/src/components/robot/RobotEdit.tsx index aa227e6ee..11f8c3a35 100644 --- a/src/components/robot/RobotEdit.tsx +++ b/src/components/robot/RobotEdit.tsx @@ -567,7 +567,7 @@ export const RobotEditModal = ({ 50 - (robot.description?.length ?? 0) <= 0 ? "red" : "inherit", }} > - {50 - (robot.description?.length ?? 0)} characters remaining + {50 - (robot.description?.length ?? 0)} / 50 } />} From 6f77e0b1d4d3271277d66f1b36206d703d303bd1 Mon Sep 17 00:00:00 2001 From: AmitChauhan63390 Date: Fri, 30 May 2025 13:38:55 +0530 Subject: [PATCH 06/10] conflict fixed --- server/src/routes/storage.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/src/routes/storage.ts b/server/src/routes/storage.ts index 829f39464..9e2fc537a 100644 --- a/server/src/routes/storage.ts +++ b/server/src/routes/storage.ts @@ -259,7 +259,7 @@ router.put('/recordings/:id', requireSignIn, async (req: AuthenticatedRequest, r // Validate input - if (!name && !limits && !credentials && !targetUrl) { + if (!name && !limit && !credentials && !targetUrl) { return res.status(400).json({ error: 'Either "name", "limits", "credentials" or "target_url" must be provided.' }); } @@ -311,8 +311,8 @@ router.put('/recordings/:id', requireSignIn, async (req: AuthenticatedRequest, r workflow = handleWorkflowActions(workflow, credentials); } - if (limits && Array.isArray(limits) && limits.length > 0) { - for (const limitInfo of limits) { + if (limit && Array.isArray(limit) && limit.length > 0) { + for (const limitInfo of limit) { const { pairIndex, actionIndex, argIndex, limit } = limitInfo; const pair = workflow[pairIndex]; From 33d4fd0639d2277f2b1215956b17cd94a9665a5b Mon Sep 17 00:00:00 2001 From: AmitChauhan63390 Date: Thu, 19 Jun 2025 14:49:52 +0530 Subject: [PATCH 07/10] translations added for description box --- public/locales/de.json | 3 ++- public/locales/en.json | 6 +++++- public/locales/es.json | 3 ++- public/locales/zh.json | 3 ++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/public/locales/de.json b/public/locales/de.json index 650e024d9..6285a5c75 100644 --- a/public/locales/de.json +++ b/public/locales/de.json @@ -249,7 +249,8 @@ }, "tooltips": { "saving": "Workflow wird optimiert und gespeichert" - } + }, + "description": "Beschreibung (optional)" }, "browser_recording": { "modal": { diff --git a/public/locales/en.json b/public/locales/en.json index 4b981a58b..ed83fff1d 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -262,7 +262,11 @@ }, "tooltips": { "saving": "Optimizing and saving the workflow" - } + }, + + "description": "Description (optional)" + + }, "browser_recording": { "modal": { diff --git a/public/locales/es.json b/public/locales/es.json index 8236157c0..d375d7198 100644 --- a/public/locales/es.json +++ b/public/locales/es.json @@ -250,7 +250,8 @@ }, "tooltips": { "saving": "Optimizando y guardando el flujo de trabajo" - } + }, + "description": "Descripción (opcional)" }, "browser_recording": { "modal": { diff --git a/public/locales/zh.json b/public/locales/zh.json index 3abcfa405..9505c83af 100644 --- a/public/locales/zh.json +++ b/public/locales/zh.json @@ -250,7 +250,8 @@ }, "tooltips": { "saving": "正在优化并保存工作流程" - } + }, + "description": "描述(可选)" }, "browser_recording": { "modal": { From 451c235624d3a4d79a7cbfe4cc5d629177f586b7 Mon Sep 17 00:00:00 2001 From: AmitChauhan63390 Date: Thu, 19 Jun 2025 14:55:25 +0530 Subject: [PATCH 08/10] removed duplcate optional comment --- src/components/recorder/SaveRecording.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/recorder/SaveRecording.tsx b/src/components/recorder/SaveRecording.tsx index 883e87ca7..ad835df92 100644 --- a/src/components/recorder/SaveRecording.tsx +++ b/src/components/recorder/SaveRecording.tsx @@ -229,7 +229,6 @@ export const SaveRecording = ({ fileName }: SaveRecordingProps) => { : "inherit", }} > - {t("common.optional")} {50 - saveRecordingDescription.length} / 50 } From 9f7b84764847378d7c975fbe078c1062f5a91755 Mon Sep 17 00:00:00 2001 From: AmitChauhan63390 Date: Tue, 24 Jun 2025 13:07:08 +0530 Subject: [PATCH 09/10] box width changed --- src/components/recorder/SaveRecording.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/recorder/SaveRecording.tsx b/src/components/recorder/SaveRecording.tsx index ad835df92..4bcccfa69 100644 --- a/src/components/recorder/SaveRecording.tsx +++ b/src/components/recorder/SaveRecording.tsx @@ -276,11 +276,11 @@ const modalStyle = { top: "25%", left: "50%", transform: "translate(-50%, -50%)", - width: "400px", + width: "500px", // Close to the old 300px field + some padding maxWidth: "90vw", bgcolor: "background.paper", - boxShadow: "24px", + boxShadow: "0px 4px 24px rgba(0,0,0,0.2)", p: 3, borderRadius: 2, display: "block", -}; \ No newline at end of file +}; From f609dfcfcd57fac3718b430fe8e0916ab162a4c5 Mon Sep 17 00:00:00 2001 From: AmitChauhan63390 Date: Tue, 24 Jun 2025 13:15:29 +0530 Subject: [PATCH 10/10] transaltions for description box added --- public/locales/de.json | 3 ++- public/locales/en.json | 3 ++- public/locales/es.json | 3 ++- public/locales/ja.json | 3 ++- public/locales/zh.json | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/public/locales/de.json b/public/locales/de.json index 6285a5c75..81c1f1407 100644 --- a/public/locales/de.json +++ b/public/locales/de.json @@ -427,7 +427,8 @@ "created_at": "Erstellungsdatum des Roboters", "errors": { "robot_not_found": "Roboterdetails konnten nicht gefunden werden. Bitte versuchen Sie es erneut." - } + }, + "description": "Beschreibung (optional)" }, "robot_edit": { "title": "Roboter bearbeiten", diff --git a/public/locales/en.json b/public/locales/en.json index ed83fff1d..5f720fba9 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -443,7 +443,8 @@ "created_at": "Robot Created At", "errors": { "robot_not_found": "Could not find robot details. Please try again." - } + }, + "description":"Description (optional)" }, "robot_edit": { "title": "Edit Robot", diff --git a/public/locales/es.json b/public/locales/es.json index d375d7198..94eb06ab0 100644 --- a/public/locales/es.json +++ b/public/locales/es.json @@ -428,7 +428,8 @@ "created_at": "Fecha de Creación del Robot", "errors": { "robot_not_found": "No se pudieron encontrar los detalles del robot. Inténtelo de nuevo." - } + }, + "description": "Descripción (opcional)" }, "robot_edit": { "title": "Editar Robot", diff --git a/public/locales/ja.json b/public/locales/ja.json index ce4b74e5e..7ae958fd4 100644 --- a/public/locales/ja.json +++ b/public/locales/ja.json @@ -427,7 +427,8 @@ "created_at": "作成日時", "errors": { "robot_not_found": "ロボットの詳細が見つかりませんでした。もう一度試してください。" - } + }, + "description": "説明(任意)" }, "robot_edit": { "title": "ロボットを編集", diff --git a/public/locales/zh.json b/public/locales/zh.json index 9505c83af..646d72258 100644 --- a/public/locales/zh.json +++ b/public/locales/zh.json @@ -428,7 +428,8 @@ "created_at": "机器人创建时间", "errors": { "robot_not_found": "无法找到机器人详细信息。请重试。" - } + }, + "description": "描述(可选)" }, "robot_edit": { "title": "编辑机器人",