diff --git a/src/components/molecules/ActionSettings.tsx b/src/components/molecules/ActionSettings.tsx new file mode 100644 index 00000000..816c3e76 --- /dev/null +++ b/src/components/molecules/ActionSettings.tsx @@ -0,0 +1,79 @@ +import React, { useRef } from 'react'; +import styled from "styled-components"; +import { Button } from "@mui/material"; +import { ActionDescription } from "../organisms/RightSidePanel"; +import * as Settings from "./action-settings"; +import { useSocketStore } from "../../context/socket"; + +interface ActionSettingsProps { + action: string; +} + +export const ActionSettings = ({ action }: ActionSettingsProps) => { + + const settingsRef = useRef<{ getSettings: () => object }>(null); + const { socket } = useSocketStore(); + + const DisplaySettings = () => { + switch (action) { + case "screenshot": + return ; + case 'scroll': + return ; + case 'scrape': + return ; + case 'scrapeSchema': + return ; + case 'script': + return ; + case 'enqueueLinks': + return ; + case 'mouse.click': + return ; + default: + return null; + } + } + + const handleSubmit = (event: React.SyntheticEvent) => { + event.preventDefault(); + //get the data from settings + const settings = settingsRef.current?.getSettings(); + //Send notification to the server and generate the pair + socket?.emit(`action`, { + action, + settings + }); + } + + return ( +
+ Action settings: + +
+ + + +
+
+ ); +}; + +const ActionSettingsWrapper = styled.div<{ action: string }>` + display: flex; + flex-direction: column; + align-items: ${({ action }) => action === 'script' ? 'stretch' : 'center'};; + justify-content: center; + margin-top: 20px; +`; diff --git a/src/components/molecules/action-settings/clickOnCoordinates.tsx b/src/components/molecules/action-settings/clickOnCoordinates.tsx new file mode 100644 index 00000000..c68a53b0 --- /dev/null +++ b/src/components/molecules/action-settings/clickOnCoordinates.tsx @@ -0,0 +1,39 @@ +import React, { forwardRef, useImperativeHandle } from 'react'; +import { Stack, TextField } from "@mui/material"; +import { WarningText } from '../../atoms/texts'; +import InfoIcon from "@mui/icons-material/Info"; + +export const ClickOnCoordinatesSettings = forwardRef((props, ref) => { + const [settings, setSettings] = React.useState([0, 0]); + useImperativeHandle(ref, () => ({ + getSettings() { + return settings; + } + })); + + return ( + + setSettings(prevState => ([Number(e.target.value), prevState[1]]))} + required + defaultValue={settings[0]} + /> + setSettings(prevState => ([prevState[0], Number(e.target.value)]))} + required + defaultValue={settings[1]} + /> + + + The click function will click on the given coordinates. + You need to put the coordinates by yourself. + + + ); +}); diff --git a/src/components/molecules/action-settings/enqueueLinks.tsx b/src/components/molecules/action-settings/enqueueLinks.tsx new file mode 100644 index 00000000..2c383d47 --- /dev/null +++ b/src/components/molecules/action-settings/enqueueLinks.tsx @@ -0,0 +1,32 @@ +import React, { forwardRef, useImperativeHandle } from 'react'; +import { Stack, TextField } from "@mui/material"; +import { WarningText } from "../../atoms/texts"; +import WarningIcon from "@mui/icons-material/Warning"; +import InfoIcon from "@mui/icons-material/Info"; + +export const EnqueueLinksSettings = forwardRef((props, ref) => { + const [settings, setSettings] = React.useState(''); + useImperativeHandle(ref, () => ({ + getSettings() { + return settings; + } + })); + + return ( + + setSettings(e.target.value)} + /> + + + Reads elements targeted by the selector and stores their links in a queue. + Those pages are then processed using the same workflow as the initial page + (in parallel if the maxConcurrency parameter is greater than 1). + + + ); +}); diff --git a/src/components/molecules/action-settings/index.ts b/src/components/molecules/action-settings/index.ts new file mode 100644 index 00000000..32906db7 --- /dev/null +++ b/src/components/molecules/action-settings/index.ts @@ -0,0 +1,17 @@ +import { ScrollSettings } from './scroll'; +import { ScreenshotSettings } from "./screenshot"; +import { ScrapeSettings } from "./scrape"; +import { ScrapeSchemaSettings } from "./scrapeSchema"; +import { ScriptSettings } from "./script"; +import { EnqueueLinksSettings } from "./enqueueLinks"; +import { ClickOnCoordinatesSettings } from "./clickOnCoordinates"; + +export { + ScrollSettings, + ScreenshotSettings, + ScrapeSettings, + ScrapeSchemaSettings, + ScriptSettings, + EnqueueLinksSettings, + ClickOnCoordinatesSettings, +}; diff --git a/src/components/molecules/action-settings/scrape.tsx b/src/components/molecules/action-settings/scrape.tsx new file mode 100644 index 00000000..80608b1e --- /dev/null +++ b/src/components/molecules/action-settings/scrape.tsx @@ -0,0 +1,30 @@ +import React, { forwardRef, useImperativeHandle } from 'react'; +import { Stack, TextField } from "@mui/material"; +import { WarningText } from '../../atoms/texts'; +import InfoIcon from "@mui/icons-material/Info"; + +export const ScrapeSettings = forwardRef((props, ref) => { + const [settings, setSettings] = React.useState(''); + useImperativeHandle(ref, () => ({ + getSettings() { + return settings; + } + })); + + return ( + + setSettings(e.target.value)} + /> + + + The scrape function uses heuristic algorithm to automatically scrape only important data from the page. + If a selector is used it will scrape and automatically parse all available + data inside of the selected element(s). + + + ); +}); diff --git a/src/components/molecules/action-settings/scrapeSchema.tsx b/src/components/molecules/action-settings/scrapeSchema.tsx new file mode 100644 index 00000000..3f60c787 --- /dev/null +++ b/src/components/molecules/action-settings/scrapeSchema.tsx @@ -0,0 +1,25 @@ +import React, { forwardRef, useCallback, useImperativeHandle, useRef } from 'react'; +import { WarningText } from "../../atoms/texts"; +import InfoIcon from "@mui/icons-material/Info"; +import { KeyValueForm } from "../KeyValueForm"; + +export const ScrapeSchemaSettings = forwardRef((props, ref) => { + const keyValueFormRef = useRef<{ getObject: () => object }>(null); + + useImperativeHandle(ref, () => ({ + getSettings() { + const settings = keyValueFormRef.current?.getObject() as Record + return settings; + } + })); + + return ( +
+ + + The interpreter scrapes the data from a webpage into a "curated" table. + + +
+ ); +}); diff --git a/src/components/molecules/action-settings/screenshot.tsx b/src/components/molecules/action-settings/screenshot.tsx new file mode 100644 index 00000000..8e412afa --- /dev/null +++ b/src/components/molecules/action-settings/screenshot.tsx @@ -0,0 +1,126 @@ +import React, { forwardRef, useImperativeHandle } from 'react'; +import { InputLabel, MenuItem, TextField, Select, FormControl } from "@mui/material"; +import { ScreenshotSettings as Settings } from "../../../shared/types"; +import styled from "styled-components"; +import { SelectChangeEvent } from "@mui/material/Select/Select"; +import { Dropdown } from "../../atoms/DropdownMui"; + +export const ScreenshotSettings = forwardRef((props, ref) => { + const [settings, setSettings] = React.useState({}); + useImperativeHandle(ref, () => ({ + getSettings() { + return settings; + } + })); + + const handleInput = (event: React.ChangeEvent) => { + const { id, value, type } = event.target; + let parsedValue: any = value; + if (type === "number") { + parsedValue = parseInt(value); + }; + setSettings({ + ...settings, + [id]: parsedValue, + }); + }; + + const handleSelect = (event: SelectChangeEvent) => { + const { name, value } = event.target; + let parsedValue: any = value; + if (value === "true" || value === "false") { + parsedValue = value === "true"; + }; + setSettings({ + ...settings, + [name]: parsedValue, + }); + }; + + return ( + + + jpeg + png + + {settings.type === "jpeg" ? + : null + } + + + disabled + allow + + {settings.type === "png" ? + + true + false + + : null + } + + hide + initial + + + true + false + + + css + device + + + ); +}); + +const SettingsWrapper = styled.div` + margin-left: 15px; + * { + margin-bottom: 10px; + } +`; diff --git a/src/components/molecules/action-settings/script.tsx b/src/components/molecules/action-settings/script.tsx new file mode 100644 index 00000000..2cece263 --- /dev/null +++ b/src/components/molecules/action-settings/script.tsx @@ -0,0 +1,63 @@ +import React, { forwardRef, useImperativeHandle } from 'react'; +import Editor from 'react-simple-code-editor'; +// @ts-ignore +import { highlight, languages } from 'prismjs/components/prism-core'; +import 'prismjs/components/prism-clike'; +import 'prismjs/components/prism-javascript'; +import 'prismjs/themes/prism.css'; +import styled from "styled-components"; +import InfoIcon from '@mui/icons-material/Info'; +import { WarningText } from "../../atoms/texts"; + +export const ScriptSettings = forwardRef((props, ref) => { + const [code, setCode] = React.useState(''); + + useImperativeHandle(ref, () => ({ + getSettings() { + return code; + } + })); + + return ( + + + + Allows to run an arbitrary asynchronous function evaluated at the server + side accepting the current page instance argument. + + setCode(code)} + highlight={code => highlight(code, languages.js)} + padding={10} + style={{ + fontFamily: '"Fira code", "Fira Mono", monospace', + fontSize: 12, + background: '#f0f0f0', + }} + /> + + ); +}); + +const EditorWrapper = styled.div` + flex: 1; + overflow: auto; + /** hard-coded height */ + height: 100%; + width: 100%; +`; + +const StyledEditor = styled(Editor)` + white-space: pre; + caret-color: #fff; + min-width: 100%; + min-height: 100%; + float: left; + & > textarea, + & > pre { + outline: none; + white-space: pre !important; + } +`; diff --git a/src/components/molecules/action-settings/scroll.tsx b/src/components/molecules/action-settings/scroll.tsx new file mode 100644 index 00000000..2e5743af --- /dev/null +++ b/src/components/molecules/action-settings/scroll.tsx @@ -0,0 +1,21 @@ +import React, { forwardRef, useImperativeHandle } from 'react'; +import { TextField } from "@mui/material"; + +export const ScrollSettings = forwardRef((props, ref) => { + const [settings, setSettings] = React.useState(0); + useImperativeHandle(ref, () => ({ + getSettings() { + return settings; + } + })); + + return ( + setSettings(parseInt(e.target.value))} + /> + ); +});