Merge pull request #336 from getmaxun/routing

feat: routing
This commit is contained in:
Karishma Shukla
2025-01-10 12:37:08 +05:30
committed by GitHub
6 changed files with 143 additions and 183 deletions

View File

@@ -2,6 +2,7 @@ import React from 'react';
import Tabs from '@mui/material/Tabs'; import Tabs from '@mui/material/Tabs';
import Tab from '@mui/material/Tab'; import Tab from '@mui/material/Tab';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import { useNavigate } from 'react-router-dom';
import { Paper, Button, useTheme } from "@mui/material"; import { Paper, Button, useTheme } from "@mui/material";
import { AutoAwesome, FormatListBulleted, VpnKey, Usb, Article, CloudQueue, Code, } from "@mui/icons-material"; import { AutoAwesome, FormatListBulleted, VpnKey, Usb, Article, CloudQueue, Code, } from "@mui/icons-material";
import { apiUrl } from "../../apiConfig"; import { apiUrl } from "../../apiConfig";
@@ -13,11 +14,13 @@ interface MainMenuProps {
handleChangeContent: (newValue: string) => void; handleChangeContent: (newValue: string) => void;
} }
export const MainMenu = ({ value = 'recordings', handleChangeContent }: MainMenuProps) => { export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProps) => {
const theme = useTheme(); const theme = useTheme();
const { t } = useTranslation(); const { t } = useTranslation();
const navigate = useNavigate();
const handleChange = (event: React.SyntheticEvent, newValue: string) => { const handleChange = (event: React.SyntheticEvent, newValue: string) => {
navigate(`/${newValue}`);
handleChangeContent(newValue); handleChangeContent(newValue);
}; };
@@ -65,7 +68,7 @@ export const MainMenu = ({ value = 'recordings', handleChangeContent }: MainMenu
textAlign: 'left', textAlign: 'left',
fontSize: 'medium', fontSize: 'medium',
}} }}
value="recordings" value="robots"
label={t('mainmenu.recordings')} label={t('mainmenu.recordings')}
icon={<AutoAwesome />} icon={<AutoAwesome />}
iconPosition="start" iconPosition="start"

View File

@@ -1,12 +1,13 @@
import React, { useState } from 'react'; import React, { useState } from "react";
import { RecordingsTable } from "./RecordingsTable"; import { RecordingsTable } from "./RecordingsTable";
import { Grid } from "@mui/material"; import { Grid } from "@mui/material";
import { RunSettings, RunSettingsModal } from "../run/RunSettings"; import { RunSettings, RunSettingsModal } from "../run/RunSettings";
import { ScheduleSettings, ScheduleSettingsModal } from "./ScheduleSettings"; import { ScheduleSettings, ScheduleSettingsModal } from "./ScheduleSettings";
import { IntegrationSettings, IntegrationSettingsModal } from "../integration/IntegrationSettings"; import { IntegrationSettings, IntegrationSettingsModal } from "../integration/IntegrationSettings";
import { RobotSettings, RobotSettingsModal } from "./RobotSettings"; import { RobotSettings, RobotSettingsModal } from "./RobotSettings";
import { RobotEditModal } from './RobotEdit'; import { RobotEditModal } from "./RobotEdit";
import { RobotDuplicationModal } from './RobotDuplicate'; import { RobotDuplicationModal } from "./RobotDuplicate";
import { useNavigate, useLocation, useParams } from "react-router-dom";
interface RecordingsProps { interface RecordingsProps {
handleEditRecording: (id: string, fileName: string) => void; handleEditRecording: (id: string, fileName: string) => void;
@@ -15,181 +16,115 @@ interface RecordingsProps {
setRecordingInfo: (id: string, name: string) => void; setRecordingInfo: (id: string, name: string) => void;
} }
export const Recordings = ({ handleEditRecording, handleRunRecording, setRecordingInfo, handleScheduleRecording }: RecordingsProps) => { export const Recordings = ({
const [runSettingsAreOpen, setRunSettingsAreOpen] = useState(false); handleEditRecording,
const [scheduleSettingsAreOpen, setScheduleSettingsAreOpen] = useState(false); handleRunRecording,
const [integrateSettingsAreOpen, setIntegrateSettingsAreOpen] = useState(false); setRecordingInfo,
const [robotSettingsAreOpen, setRobotSettingsAreOpen] = useState(false); handleScheduleRecording,
const [robotEditAreOpen, setRobotEditAreOpen] = useState(false); }: RecordingsProps) => {
const [robotDuplicateAreOpen, setRobotDuplicateAreOpen] = useState(false); const navigate = useNavigate();
const location = useLocation();
const { selectedRecordingId } = useParams();
const [params, setParams] = useState<string[]>([]); const [params, setParams] = useState<string[]>([]);
const [selectedRecordingId, setSelectedRecordingId] = useState<string>('');
const handleIntegrateRecording = (id: string, settings: IntegrationSettings) => { };
const handleSettingsRecording = (id: string, settings: RobotSettings) => { };
const handleEditRobot = (id: string, settings: RobotSettings) => { };
const handleDuplicateRobot = (id: string, settings: RobotSettings) => { };
const handleSettingsAndIntegrate = (id: string, name: string, params: string[]) => { const handleNavigate = (path: string, id: string, name: string, params: string[]) => {
if (params.length === 0) { setParams(params);
setIntegrateSettingsAreOpen(true); setRecordingInfo(id, name);
setRecordingInfo(id, name); navigate(path);
setSelectedRecordingId(id); };
} else {
setParams(params);
setIntegrateSettingsAreOpen(true);
setRecordingInfo(id, name);
setSelectedRecordingId(id);
}
}
const handleSettingsAndRun = (id: string, name: string, params: string[]) => {
if (params.length === 0) {
setRunSettingsAreOpen(true);
setRecordingInfo(id, name);
setSelectedRecordingId(id);
} else {
setParams(params);
setRunSettingsAreOpen(true);
setRecordingInfo(id, name);
setSelectedRecordingId(id);
}
}
const handleSettingsAndSchedule = (id: string, name: string, params: string[]) => {
if (params.length === 0) {
setScheduleSettingsAreOpen(true);
setRecordingInfo(id, name);
setSelectedRecordingId(id);
} else {
setParams(params);
setScheduleSettingsAreOpen(true);
setRecordingInfo(id, name);
setSelectedRecordingId(id);
}
}
const handleRobotSettings = (id: string, name: string, params: string[]) => {
if (params.length === 0) {
setRobotSettingsAreOpen(true);
setRecordingInfo(id, name);
setSelectedRecordingId(id);
} else {
setParams(params);
setRobotSettingsAreOpen(true);
setRecordingInfo(id, name);
setSelectedRecordingId(id);
}
}
const handleEditRobotOption = (id: string, name: string, params: string[]) => {
if (params.length === 0) {
setRobotEditAreOpen(true);
setRecordingInfo(id, name);
setSelectedRecordingId(id);
} else {
setParams(params);
setRobotEditAreOpen(true);
setRecordingInfo(id, name);
setSelectedRecordingId(id);
}
}
const handleDuplicateRobotOption = (id: string, name: string, params: string[]) => {
if (params.length === 0) {
setRobotDuplicateAreOpen(true);
setRecordingInfo(id, name);
setSelectedRecordingId(id);
} else {
setParams(params);
setRobotDuplicateAreOpen(true);
setRecordingInfo(id, name);
setSelectedRecordingId(id);
}
}
const handleClose = () => { const handleClose = () => {
setParams([]); setParams([]);
setRunSettingsAreOpen(false); setRecordingInfo("", "");
setRecordingInfo('', ''); navigate("/robots"); // Navigate back to the main robots page
setSelectedRecordingId(''); };
}
const handleIntegrateClose = () => { // Determine which modal to open based on the current route
setParams([]); const getCurrentModal = () => {
setIntegrateSettingsAreOpen(false); const currentPath = location.pathname;
setRecordingInfo('', '');
setSelectedRecordingId('');
}
const handleScheduleClose = () => { if (currentPath.endsWith("/run")) {
setParams([]); return (
setScheduleSettingsAreOpen(false); <RunSettingsModal
setRecordingInfo('', ''); isOpen={true}
setSelectedRecordingId(''); handleClose={handleClose}
} handleStart={handleRunRecording}
isTask={params.length !== 0}
const handleRobotSettingsClose = () => { params={params}
setParams([]); />
setRobotSettingsAreOpen(false); );
setRecordingInfo('', ''); } else if (currentPath.endsWith("/schedule")) {
setSelectedRecordingId(''); return (
} <ScheduleSettingsModal
isOpen={true}
const handleRobotEditClose = () => { handleClose={handleClose}
setParams([]); handleStart={handleScheduleRecording}
setRobotEditAreOpen(false); />
setRecordingInfo('', ''); );
setSelectedRecordingId(''); } else if (currentPath.endsWith("/integrate")) {
} return (
<IntegrationSettingsModal
const handleRobotDuplicateClose = () => { isOpen={true}
setParams([]); handleClose={handleClose}
setRobotDuplicateAreOpen(false); handleStart={() => {}}
setRecordingInfo('', ''); />
setSelectedRecordingId(''); );
} } else if (currentPath.endsWith("/settings")) {
return (
<RobotSettingsModal
isOpen={true}
handleClose={handleClose}
handleStart={() => {}}
/>
);
} else if (currentPath.endsWith("/edit")) {
return (
<RobotEditModal
isOpen={true}
handleClose={handleClose}
handleStart={() => {}}
/>
);
} else if (currentPath.endsWith("/duplicate")) {
return (
<RobotDuplicationModal
isOpen={true}
handleClose={handleClose}
handleStart={() => {}}
/>
);
}
return null;
};
return ( return (
<React.Fragment> <React.Fragment>
<RunSettingsModal isOpen={runSettingsAreOpen} {getCurrentModal()}
handleClose={handleClose} <Grid container direction="column" sx={{ padding: "30px" }}>
handleStart={(settings) => handleRunRecording(settings)}
isTask={params.length !== 0}
params={params}
/>
<ScheduleSettingsModal isOpen={scheduleSettingsAreOpen}
handleClose={handleScheduleClose}
handleStart={(settings) => handleScheduleRecording(settings)}
/>
<IntegrationSettingsModal isOpen={integrateSettingsAreOpen}
handleClose={handleIntegrateClose}
handleStart={(settings) => handleIntegrateRecording(selectedRecordingId, settings)}
/>
<RobotSettingsModal isOpen={robotSettingsAreOpen}
handleClose={handleRobotSettingsClose}
handleStart={(settings) => handleSettingsRecording(selectedRecordingId, settings)}
/>
<RobotEditModal isOpen={robotEditAreOpen}
handleClose={handleRobotEditClose}
handleStart={(settings) => handleEditRobot(selectedRecordingId, settings)}
/>
<RobotDuplicationModal isOpen={robotDuplicateAreOpen}
handleClose={handleRobotDuplicateClose}
handleStart={(settings) => handleDuplicateRobot(selectedRecordingId, settings)}
/>
<Grid container direction="column" sx={{ padding: '30px' }}>
<Grid item xs> <Grid item xs>
<RecordingsTable <RecordingsTable
handleEditRecording={handleEditRecording} handleEditRecording={handleEditRecording}
handleRunRecording={handleSettingsAndRun} handleRunRecording={(id, name, params) =>
handleScheduleRecording={handleSettingsAndSchedule} handleNavigate(`/robots/${id}/run`, id, name, params)
handleIntegrateRecording={handleSettingsAndIntegrate} }
handleSettingsRecording={handleRobotSettings} handleScheduleRecording={(id, name, params) =>
handleEditRobot={handleEditRobotOption} handleNavigate(`/robots/${id}/schedule`, id, name, params)
handleDuplicateRobot={handleDuplicateRobotOption} }
handleIntegrateRecording={(id, name, params) =>
handleNavigate(`/robots/${id}/integrate`, id, name, params)
}
handleSettingsRecording={(id, name, params) =>
handleNavigate(`/robots/${id}/settings`, id, name, params)
}
handleEditRobot={(id, name, params) =>
handleNavigate(`/robots/${id}/edit`, id, name, params)
}
handleDuplicateRobot={(id, name, params) =>
handleNavigate(`/robots/${id}/duplicate`, id, name, params)
}
/> />
</Grid> </Grid>
</Grid> </Grid>
</React.Fragment> </React.Fragment>
); );
} };

View File

@@ -11,6 +11,7 @@ import { GenericModal } from "../ui/GenericModal";
import { modalStyle } from "../recorder/AddWhereCondModal"; import { modalStyle } from "../recorder/AddWhereCondModal";
import { getUserById } from "../../api/auth"; import { getUserById } from "../../api/auth";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
interface RunTypeChipProps { interface RunTypeChipProps {
runByUserId?: string; runByUserId?: string;
@@ -37,6 +38,7 @@ interface CollapsibleRowProps {
} }
export const CollapsibleRow = ({ row, handleDelete, isOpen, currentLog, abortRunHandler, runningRecordingName }: CollapsibleRowProps) => { export const CollapsibleRow = ({ row, handleDelete, isOpen, currentLog, abortRunHandler, runningRecordingName }: CollapsibleRowProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const navigate = useNavigate();
const [open, setOpen] = useState(isOpen); const [open, setOpen] = useState(isOpen);
const [openSettingsModal, setOpenSettingsModal] = useState(false); const [openSettingsModal, setOpenSettingsModal] = useState(false);
const [userEmail, setUserEmail] = useState<string | null>(null); const [userEmail, setUserEmail] = useState<string | null>(null);
@@ -47,7 +49,7 @@ export const CollapsibleRow = ({ row, handleDelete, isOpen, currentLog, abortRun
: row.runByAPI : row.runByAPI
? 'API' ? 'API'
: 'Unknown'; : 'Unknown';
const logEndRef = useRef<HTMLDivElement | null>(null); const logEndRef = useRef<HTMLDivElement | null>(null);
const scrollToLogBottom = () => { const scrollToLogBottom = () => {
@@ -60,9 +62,20 @@ export const CollapsibleRow = ({ row, handleDelete, isOpen, currentLog, abortRun
abortRunHandler(); abortRunHandler();
} }
useEffect(() => { const handleRowExpand = () => {
scrollToLogBottom(); const newOpen = !open;
}, [currentLog]) setOpen(newOpen);
if (newOpen) {
navigate(`/runs/${row.robotMetaId}/run/${row.runId}`);
} else {
navigate(`/runs/${row.robotMetaId}`);
}
//scrollToLogBottom();
};
// useEffect(() => {
// scrollToLogBottom();
// }, [currentLog])
useEffect(() => { useEffect(() => {
const fetchUserEmail = async () => { const fetchUserEmail = async () => {
@@ -83,10 +96,7 @@ export const CollapsibleRow = ({ row, handleDelete, isOpen, currentLog, abortRun
<IconButton <IconButton
aria-label="expand row" aria-label="expand row"
size="small" size="small"
onClick={() => { onClick={handleRowExpand}
setOpen(!open);
scrollToLogBottom();
}}
> >
{open ? <KeyboardArrowUp /> : <KeyboardArrowDown />} {open ? <KeyboardArrowUp /> : <KeyboardArrowDown />}
</IconButton> </IconButton>

View File

@@ -12,7 +12,7 @@ import TableRow from '@mui/material/TableRow';
import { Accordion, AccordionSummary, AccordionDetails, Typography, Box, TextField } from '@mui/material'; import { Accordion, AccordionSummary, AccordionDetails, Typography, Box, TextField } from '@mui/material';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import SearchIcon from '@mui/icons-material/Search'; import SearchIcon from '@mui/icons-material/Search';
import { useNavigate } from 'react-router-dom';
import { useGlobalInfoStore } from "../../context/globalInfo"; import { useGlobalInfoStore } from "../../context/globalInfo";
import { getStoredRuns } from "../../api/storage"; import { getStoredRuns } from "../../api/storage";
import { RunSettings } from "./RunSettings"; import { RunSettings } from "./RunSettings";
@@ -68,6 +68,7 @@ export const RunsTable: React.FC<RunsTableProps> = ({
runningRecordingName runningRecordingName
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const navigate = useNavigate();
// Update column labels using translation if needed // Update column labels using translation if needed
const translatedColumns = columns.map(column => ({ const translatedColumns = columns.map(column => ({
@@ -82,6 +83,14 @@ export const RunsTable: React.FC<RunsTableProps> = ({
const { notify, rerenderRuns, setRerenderRuns } = useGlobalInfoStore(); const { notify, rerenderRuns, setRerenderRuns } = useGlobalInfoStore();
const handleAccordionChange = (robotMetaId: string, isExpanded: boolean) => {
if (isExpanded) {
navigate(`/runs/${robotMetaId}`);
} else {
navigate(`/runs`);
}
};
const handleChangePage = (event: unknown, newPage: number) => { const handleChangePage = (event: unknown, newPage: number) => {
setPage(newPage); setPage(newPage);
}; };
@@ -155,7 +164,7 @@ export const RunsTable: React.FC<RunsTableProps> = ({
</Box> </Box>
<TableContainer component={Paper} sx={{ width: '100%', overflow: 'hidden' }}> <TableContainer component={Paper} sx={{ width: '100%', overflow: 'hidden' }}>
{Object.entries(groupedRows).map(([id, data]) => ( {Object.entries(groupedRows).map(([id, data]) => (
<Accordion key={id}> <Accordion key={id} onChange={(event, isExpanded) => handleAccordionChange(id, isExpanded)}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}> <AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography variant="h6">{data[data.length - 1].name}</Typography> <Typography variant="h6">{data[data.length - 1].name}</Typography>
</AccordionSummary> </AccordionSummary>

View File

@@ -18,6 +18,7 @@ import { apiUrl } from "../apiConfig";
interface MainPageProps { interface MainPageProps {
handleEditRecording: (id: string, fileName: string) => void; handleEditRecording: (id: string, fileName: string) => void;
initialContent: string;
} }
export interface CreateRunResponse { export interface CreateRunResponse {
@@ -30,9 +31,9 @@ export interface ScheduleRunResponse {
runId: string; runId: string;
} }
export const MainPage = ({ handleEditRecording }: MainPageProps) => { export const MainPage = ({ handleEditRecording, initialContent }: MainPageProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [content, setContent] = React.useState('recordings'); const [content, setContent] = React.useState(initialContent);
const [sockets, setSockets] = React.useState<Socket[]>([]); const [sockets, setSockets] = React.useState<Socket[]>([]);
const [runningRecordingId, setRunningRecordingId] = React.useState(''); const [runningRecordingId, setRunningRecordingId] = React.useState('');
const [runningRecordingName, setRunningRecordingName] = React.useState(''); const [runningRecordingName, setRunningRecordingName] = React.useState('');
@@ -123,7 +124,7 @@ export const MainPage = ({ handleEditRecording }: MainPageProps) => {
const DisplayContent = () => { const DisplayContent = () => {
switch (content) { switch (content) {
case 'recordings': case 'robots':
return <Recordings return <Recordings
handleEditRecording={handleEditRecording} handleEditRecording={handleEditRecording}
handleRunRecording={handleRunRecording} handleRunRecording={handleRunRecording}

View File

@@ -11,8 +11,8 @@ import { AlertSnackbar } from "../components/ui/AlertSnackbar";
import Login from './Login'; import Login from './Login';
import Register from './Register'; import Register from './Register';
import UserRoute from '../routes/userRoute'; import UserRoute from '../routes/userRoute';
import { Routes, Route, useNavigate } from 'react-router-dom'; import { Routes, Route, useNavigate, Navigate } from 'react-router-dom';
import { AppBar } from '@mui/material'; import { Runs } from '../components/run/Runs';
export const PageWrapper = () => { export const PageWrapper = () => {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
@@ -51,12 +51,14 @@ export const PageWrapper = () => {
<AuthProvider> <AuthProvider>
<SocketProvider> <SocketProvider>
<React.Fragment> <React.Fragment>
{!browserId && <NavBar recordingName={recordingName} isRecording={!!browserId} />} {!browserId && <NavBar recordingName={recordingName} isRecording={!!browserId} />}
<Routes> <Routes>
<Route element={<UserRoute />}> <Route element={<UserRoute />}>
<Route path="/" element={<MainPage handleEditRecording={handleEditRecording} />} /> <Route path="/" element={<Navigate to="/robots" replace />} />
<Route path="/robots/*" element={<MainPage handleEditRecording={handleEditRecording} initialContent="robots" />} />
<Route path="/runs/*" element={<MainPage handleEditRecording={handleEditRecording} initialContent="runs" />} />
<Route path="/proxy" element={<MainPage handleEditRecording={handleEditRecording} initialContent="proxy" />} />
<Route path="/apikey" element={<MainPage handleEditRecording={handleEditRecording} initialContent="apikey" />} />
</Route> </Route>
<Route element={<UserRoute />}> <Route element={<UserRoute />}>
<Route path="/recording" element={ <Route path="/recording" element={