-
Notifications
You must be signed in to change notification settings - Fork 1.1k
refactor: move robot modals to legacy #820
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
import React, { useState, useEffect } from 'react'; | ||
import { GenericModal } from "../ui/GenericModal"; | ||
import { TextField, Typography, Box, Button } from "@mui/material"; | ||
import { modalStyle } from "../recorder/AddWhereCondModal"; | ||
import { useGlobalInfoStore } from '../../context/globalInfo'; | ||
import { duplicateRecording, getStoredRecording } from '../../api/storage'; | ||
import { WhereWhatPair } from 'maxun-core'; | ||
import { useTranslation } from 'react-i18next'; | ||
|
||
interface RobotMeta { | ||
name: string; | ||
id: string; | ||
createdAt: string; | ||
pairs: number; | ||
updatedAt: string; | ||
params: any[]; | ||
} | ||
|
||
interface RobotWorkflow { | ||
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; | ||
} | ||
|
||
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; | ||
} | ||
|
||
interface RobotSettingsProps { | ||
isOpen: boolean; | ||
handleStart: (settings: RobotSettings) => void; | ||
handleClose: () => void; | ||
initialSettings?: RobotSettings | null; | ||
|
||
} | ||
|
||
export const RobotDuplicationModal = ({ isOpen, handleStart, handleClose, initialSettings }: RobotSettingsProps) => { | ||
const { t } = useTranslation(); | ||
const [targetUrl, setTargetUrl] = useState<string | undefined>(''); | ||
const [robot, setRobot] = useState<RobotSettings | null>(null); | ||
const { recordingId, notify, setRerenderRobots } = useGlobalInfoStore(); | ||
|
||
useEffect(() => { | ||
if (isOpen) { | ||
getRobot(); | ||
} | ||
}, [isOpen]); | ||
|
||
useEffect(() => { | ||
if (robot) { | ||
const lastPair = robot?.recording.workflow[robot?.recording.workflow.length - 1]; | ||
const url = lastPair?.what.find(action => action.action === "goto")?.args?.[0]; | ||
setTargetUrl(url); | ||
} | ||
}, [robot]); | ||
|
||
const getRobot = async () => { | ||
if (recordingId) { | ||
const robot = await getStoredRecording(recordingId); | ||
setRobot(robot); | ||
} else { | ||
notify('error', t('robot_duplication.notifications.robot_not_found')); | ||
} | ||
} | ||
|
||
const handleTargetUrlChange = (e: React.ChangeEvent<HTMLInputElement>) => { | ||
setTargetUrl(e.target.value); | ||
}; | ||
|
||
const handleSave = async () => { | ||
if (!robot || !targetUrl) { | ||
notify('error', t('robot_duplication.notifications.url_required')); | ||
return; | ||
} | ||
|
||
try { | ||
const success = await duplicateRecording(robot.recording_meta.id, targetUrl); | ||
|
||
if (success) { | ||
setRerenderRobots(true); | ||
|
||
notify('success', t('robot_duplication.notifications.duplicate_success')); | ||
handleStart(robot); | ||
handleClose(); | ||
} else { | ||
notify('error', t('robot_duplication.notifications.duplicate_error')); | ||
} | ||
} catch (error) { | ||
notify('error', t('robot_duplication.notifications.unknown_error')); | ||
console.error('Error updating Target URL:', error); | ||
} | ||
}; | ||
|
||
return ( | ||
<GenericModal | ||
isOpen={isOpen} | ||
onClose={handleClose} | ||
modalStyle={modalStyle} | ||
> | ||
<> | ||
<Typography variant="h5" style={{ marginBottom: '20px' }}> | ||
{t('robot_duplication.title')} | ||
</Typography> | ||
<Box style={{ display: 'flex', flexDirection: 'column' }}> | ||
{ | ||
robot && ( | ||
<> | ||
<span> | ||
{t('robot_duplication.descriptions.purpose')} | ||
</span> | ||
<br /> | ||
<span dangerouslySetInnerHTML={{ | ||
__html: t('robot_duplication.descriptions.example', { | ||
url1: '<code>producthunt.com/topics/api</code>', | ||
url2: '<code>producthunt.com/topics/database</code>' | ||
}) | ||
}} /> | ||
<br /> | ||
Comment on lines
+128
to
+137
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove Injecting translation strings through -import { useTranslation } from 'react-i18next';
+import { Trans, useTranslation } from 'react-i18next';
...
- <span dangerouslySetInnerHTML={{
- __html: t('robot_duplication.descriptions.example', {
- url1: '<code>producthunt.com/topics/api</code>',
- url2: '<code>producthunt.com/topics/database</code>'
- })
- }} />
+ <Trans
+ i18nKey="robot_duplication.descriptions.example"
+ components={{ code1: <code />, code2: <code /> }}
+ values={{
+ url1: 'producthunt.com/topics/api',
+ url2: 'producthunt.com/topics/database'
+ }}
+ /> 🧰 Tools🪛 ast-grep (0.39.5)[warning] 130-130: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks. (react-unsafe-html-injection) 🪛 Biome (2.1.2)[error] 131-131: Avoid passing content using the dangerouslySetInnerHTML prop. Setting content using code can expose users to cross-site scripting (XSS) attacks (lint/security/noDangerouslySetInnerHtml) 🤖 Prompt for AI Agents
|
||
<span> | ||
<b>{t('robot_duplication.descriptions.warning')}</b> | ||
</span> | ||
<TextField | ||
label={t('robot_duplication.fields.target_url')} | ||
key={t('robot_duplication.fields.target_url')} | ||
value={targetUrl} | ||
onChange={handleTargetUrlChange} | ||
style={{ marginBottom: '20px', marginTop: '30px' }} | ||
/> | ||
<Box mt={2} display="flex" justifyContent="flex-end"> | ||
<Button variant="contained" color="primary" onClick={handleSave}> | ||
{t('robot_duplication.buttons.duplicate')} | ||
</Button> | ||
<Button | ||
onClick={handleClose} | ||
color="primary" | ||
variant="outlined" | ||
style={{ marginLeft: '10px' }} | ||
sx={{ | ||
color: '#ff00c3 !important', | ||
borderColor: '#ff00c3 !important', | ||
backgroundColor: 'whitesmoke !important', | ||
}} > | ||
{t('robot_duplication.buttons.cancel')} | ||
</Button> | ||
</Box> | ||
</> | ||
) | ||
} | ||
</Box> | ||
</> | ||
</GenericModal> | ||
); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Keep the target URL input controlled
targetUrl
flips between''
andundefined
, so the<TextField>
toggles from controlled to uncontrolled, triggering React warnings. Keep the state as a string and normalize the fetched URL to''
when absent.📝 Committable suggestion
🤖 Prompt for AI Agents