From 6f365be7aa452998f553084204e129068195a6f3 Mon Sep 17 00:00:00 2001 From: tomondre Date: Tue, 29 Jul 2025 09:55:45 +0200 Subject: [PATCH 1/3] fix: improve backward compatibility --- reana_jupyterlab/__init__.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/reana_jupyterlab/__init__.py b/reana_jupyterlab/__init__.py index d346e27..2cd3f1e 100644 --- a/reana_jupyterlab/__init__.py +++ b/reana_jupyterlab/__init__.py @@ -18,4 +18,11 @@ def _jupyter_labextension_paths(): def _jupyter_server_extension_points(): # pragma: no cover return [{ "module": "reana_jupyterlab.server" - }] \ No newline at end of file + }] + +# Import and expose the server extension loading function for backward compatibility +# Fixes `X Validation failed: validation failed` error + +from .server import _load_jupyter_server_extension + +load_jupyter_server_extension = _load_jupyter_server_extension From 038f993e66397aebb4887762d380add63b47fedf Mon Sep 17 00:00:00 2001 From: tomondre Date: Tue, 29 Jul 2025 09:56:00 +0200 Subject: [PATCH 2/3] docs: enhance installation instructions --- README.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 044a64f..e600d8c 100644 --- a/README.md +++ b/README.md @@ -20,12 +20,12 @@ Reana JupyterLab plugin provides a set of tools to interact with the [Reana](htt ## Installation guide for users To install the extension, run the following command: ```bash -pip install reana-jupyterlab +python -m pip install reana-jupyterlab ``` In case you want to run the tests, install the extension with the following command: ```bash -pip install reana-jupyterlab[dev] +python -m pip install reana-jupyterlab[dev] ``` ### Docker image @@ -52,17 +52,18 @@ Build the extension jlpm run build ``` -Install the extension (including the testing dependencies) +Install the extension in editable mode and include the development dependencies: ```bash -python -m pip install .[dev] +python -m pip install -e . +python -m pip install ".[dev]" ``` Enable the server extension ```bash -jupyter server extension enable --py reana_jupyterlab +python -m jupyter server extension enable --py reana_jupyterlab.server ``` Finally, open a JupyterLab instance. The extension should be available in the JupyterLab sidebar. ```bash -jupyter lab -``` \ No newline at end of file +python -m jupyter lab +``` From a4cefe483e9db3c82789bda867d172f7234a8d92 Mon Sep 17 00:00:00 2001 From: tomondre Date: Tue, 29 Jul 2025 12:37:40 +0200 Subject: [PATCH 3/3] WIP! --- reana_jupyterlab/client.py | 37 ++++++ reana_jupyterlab/handlers/__init__.py | 2 +- reana_jupyterlab/handlers/connection.py | 50 -------- reana_jupyterlab/handlers/handlers.py | 13 +- reana_jupyterlab/handlers/workflows.py | 79 +++++++----- reana_jupyterlab/server.py | 2 +- src/components/@Connection/ConnectionForm.tsx | 119 ------------------ src/components/MenuBar.tsx | 8 +- src/stores/UIStore.ts | 20 +-- src/types.ts | 13 +- src/utils/ApiRequest.ts | 2 +- src/widgets/SidebarPanel.tsx | 74 ++--------- 12 files changed, 109 insertions(+), 310 deletions(-) create mode 100644 reana_jupyterlab/client.py delete mode 100644 reana_jupyterlab/handlers/connection.py delete mode 100644 src/components/@Connection/ConnectionForm.tsx diff --git a/reana_jupyterlab/client.py b/reana_jupyterlab/client.py new file mode 100644 index 0000000..0001a69 --- /dev/null +++ b/reana_jupyterlab/client.py @@ -0,0 +1,37 @@ +import os +import requests + +class ReanaAPIClient: + def __init__(self): + self.server_url = os.getenv('REANA_SERVER_URL', '') + self.access_token = os.getenv('REANA_ACCESS_TOKEN', '') + + def _get_headers(self): + return { + 'Authorization': f'Bearer {self.access_token}' + } + + def get(self, endpoint, params=None): + if params is None: + params = {} + + headers = self._get_headers() + response = requests.get( + f"{self.server_url}/api/{endpoint}", + headers=headers, + params=params + ) + return response + + def post(self, endpoint, json=None, params=None): + if params is None: + params = {} + + headers = self._get_headers() + response = requests.post( + f"{self.server_url}/api/{endpoint}", + headers=headers, + json=json, + params=params + ) + return response diff --git a/reana_jupyterlab/handlers/__init__.py b/reana_jupyterlab/handlers/__init__.py index 3df9c58..d7ed779 100644 --- a/reana_jupyterlab/handlers/__init__.py +++ b/reana_jupyterlab/handlers/__init__.py @@ -1 +1 @@ -from .handlers import * \ No newline at end of file +from .handlers import * diff --git a/reana_jupyterlab/handlers/connection.py b/reana_jupyterlab/handlers/connection.py deleted file mode 100644 index 2c6f195..0000000 --- a/reana_jupyterlab/handlers/connection.py +++ /dev/null @@ -1,50 +0,0 @@ -from jupyter_server.base.handlers import APIHandler -import os -import json -import requests - -endpoint = 'you' -class EnvVariablesHandler(APIHandler): - def _update_env(self, access_token='', server=''): - os.environ['REANA_SERVER_URL'] = server - os.environ['REANA_ACCESS_TOKEN'] = access_token - - def get(self): - server = os.getenv('REANA_SERVER_URL', '') - access_token = os.getenv('REANA_ACCESS_TOKEN', '') - - self.finish(json.dumps({ - 'server': server, - 'accessToken': access_token - })) - - def post(self): - data = self.get_json_body() - - try: - server = data.get('server', '') - access_token = data.get('accessToken', '') - - self._update_env(access_token, server) - response = requests.get(f"{server}/api/{endpoint}?access_token={access_token}") - - data = response.json() - if response.status_code != 200 or 'reana_server_version' not in data: - self._update_env() - self.finish(json.dumps({ - 'status': 'error', - 'message': f'Could not connect to the REANA server. {data.get("message", "Server not valid.").capitalize()}' - })) - else: - self.finish(json.dumps({ - 'status': 'success', - 'message': 'Credentials saved successfully. Please close any running terminals to apply the changes.' - })) - - - except Exception as e: - print(e) - self.finish(json.dumps({ - 'status': 'error', - 'message': 'Could not connect to the REANA server. Please check the server URL and access token.' - })) \ No newline at end of file diff --git a/reana_jupyterlab/handlers/handlers.py b/reana_jupyterlab/handlers/handlers.py index b17f8d4..7ff9596 100644 --- a/reana_jupyterlab/handlers/handlers.py +++ b/reana_jupyterlab/handlers/handlers.py @@ -1,15 +1,15 @@ -from jupyter_server.utils import url_path_join from jupyter_server.serverapp import ServerApp -from .connection import EnvVariablesHandler +from jupyter_server.utils import url_path_join from .files import FileBrowserHandler + from .workflows import ( WorkflowsHandler, - WorkflowLogsHandler, - WorkflowWorkspaceHandler, + WorkflowLogsHandler, + WorkflowWorkspaceHandler, WorkflowSpecificationHandler, WorkspaceFilesHandler, WorkflowCreateHandler, - WorkflowValidateHandler, + WorkflowValidateHandler ) def setup_handlers(server_app: ServerApp) -> None: @@ -17,7 +17,6 @@ def setup_handlers(server_app: ServerApp) -> None: host_pattern = ".*$" base_url = url_path_join(web_app.settings["base_url"], "reana_jupyterlab") handlers = [ - (url_path_join(base_url, "env"), EnvVariablesHandler), (url_path_join(base_url, "workflows", "([^/]+)", "workspace", "([^/]+)"), WorkspaceFilesHandler), (url_path_join(base_url, "workflows", "([^/]+)", "workspace"), WorkflowWorkspaceHandler), (url_path_join(base_url, "workflows", "([^/]+)", "logs"), WorkflowLogsHandler), @@ -27,4 +26,4 @@ def setup_handlers(server_app: ServerApp) -> None: (url_path_join(base_url, "validate"), WorkflowValidateHandler), (url_path_join(base_url, "files"), FileBrowserHandler), ] - web_app.add_handlers(host_pattern, handlers) \ No newline at end of file + web_app.add_handlers(host_pattern, handlers) diff --git a/reana_jupyterlab/handlers/workflows.py b/reana_jupyterlab/handlers/workflows.py index b5463c8..edea45d 100644 --- a/reana_jupyterlab/handlers/workflows.py +++ b/reana_jupyterlab/handlers/workflows.py @@ -2,11 +2,10 @@ import os import json import re -import requests import subprocess from urllib.parse import quote_plus, urlencode +from ..client import ReanaAPIClient -# import ../const.py file from ..const import ( WORKFLOWS_PAGE_SIZE, WORKFLOWS_TYPE, @@ -16,6 +15,10 @@ endpoint = 'workflows' class WorkflowsHandler(APIHandler): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.client = ReanaAPIClient() + def _parse_workflow(self, workflow): parsed_workflow = {} @@ -31,7 +34,7 @@ def _parse_workflow(self, workflow): parsed_workflow['totalJobs'] = workflow.get('progress').get('total', {}).get('total', 0) return parsed_workflow - + def _parse_workflows(self, workflows): parsed_workflows = [] @@ -53,7 +56,6 @@ def _parse_params(self, params): if 'search' in params: params['search'] = json.dumps({'name': [params['search']]}) - params['access_token'] = os.getenv('REANA_ACCESS_TOKEN', '') params['type'] = WORKFLOWS_TYPE params['size'] = WORKFLOWS_PAGE_SIZE params['include_progress'] = True @@ -65,10 +67,9 @@ def _parse_params(self, params): def get(self): params = self.request.query_arguments string_params = self._parse_params(params) - server_url = os.getenv('REANA_SERVER_URL', '') try: - response = requests.get(f"{server_url}/api/{endpoint}?{string_params}") + response = self.client.get(endpoint, params=string_params) workflows = self._parse_workflows(response) self.finish(json.dumps(workflows)) except Exception as e: @@ -78,6 +79,10 @@ def get(self): })) class WorkflowLogsHandler(APIHandler): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.client = ReanaAPIClient() + def _parse_logs(self, workflow): wf = workflow.json() logs = json.loads(wf.get('logs', '')) @@ -87,16 +92,12 @@ def _parse_logs(self, workflow): 'jobLogs': logs['job_logs'] } ) - - def get(self, workflow_id): - server_url = os.getenv('REANA_SERVER_URL', '') - access_token = os.getenv('REANA_ACCESS_TOKEN', '') + def get(self, workflow_id): try: - response = requests.get(f"{server_url}/api/{endpoint}/{workflow_id}/logs?access_token={access_token}") + response = self.client.get(f"{endpoint}/{workflow_id}/logs") logs = self._parse_logs(response) self.finish(logs) - except Exception as e: self.finish(json.dumps({ 'status': 'error', @@ -104,6 +105,10 @@ def get(self, workflow_id): })) class WorkflowWorkspaceHandler(APIHandler): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.client = ReanaAPIClient() + def _parse_files(self, files): parsed_files = [] @@ -117,32 +122,30 @@ def _parse_files(self, files): ) return parsed_files - + def _parse_params(self, params): params = {key: params[key][0].decode('utf-8') for key in params if not key.isdigit()} if 'search' in params: params['search'] = json.dumps({'name': [params['search']]}) - params['access_token'] = os.getenv('REANA_ACCESS_TOKEN', '') params['size'] = WORKSPACE_PAGE_SIZE string_params = urlencode(params, quote_via=quote_plus) return string_params - + def get(self, workflow_id): params = self.request.query_arguments string_params = self._parse_params(params) - server_url = os.getenv('REANA_SERVER_URL', '') try: - response = requests.get(f"{server_url}/api/{endpoint}/{workflow_id}/workspace?{string_params}") + response = self.client.get(f"{endpoint}/{workflow_id}/workspace", params=string_params) data = response.json() if response.status_code != 200: raise Exception(data.get('message', 'Error getting workspace files')) - + data['files'] = self._parse_files(data.pop('items')) self.finish(data) except Exception as e: @@ -152,12 +155,13 @@ def get(self, workflow_id): })) class WorkflowSpecificationHandler(APIHandler): - def get(self, workflow_id): - server_url = os.getenv('REANA_SERVER_URL', '') - access_token = os.getenv('REANA_ACCESS_TOKEN', '') + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.client = ReanaAPIClient() + def get(self, workflow_id): try: - response = requests.get(f"{server_url}/api/{endpoint}/{workflow_id}/specification?access_token={access_token}") + response = self.client.get(f"{endpoint}/{workflow_id}/specification") self.finish(response.json()) except Exception as e: self.finish(json.dumps({ @@ -166,17 +170,18 @@ def get(self, workflow_id): })) class WorkspaceFilesHandler(APIHandler): - def get(self, workflow_name, file_name): - server_url = os.getenv('REANA_SERVER_URL', '') - access_token = os.getenv('REANA_ACCESS_TOKEN', '') + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.client = ReanaAPIClient() + def get(self, workflow_name, file_name): try: file = quote_plus(file_name) - response = requests.get(f"{server_url}/api/{endpoint}/{workflow_name}/workspace/{file}?access_token={access_token}") + response = self.client.get(f"{endpoint}/{workflow_name}/workspace/{file}") path = file_name.rsplit('/', 1) path = path[0] if len(path) > 1 else '' - + os.makedirs(workflow_name + '/' + path, exist_ok=True) with open(workflow_name + '/' + file_name, 'wb') as f: @@ -193,6 +198,10 @@ def get(self, workflow_name, file_name): class WorkflowCreateHandler(APIHandler): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.client = ReanaAPIClient() + def post(self): try: body = json.loads(self.request.body) @@ -205,16 +214,16 @@ def post(self): if '..' in path or not os.path.isdir(workspace) or not yaml_file.endswith('.yaml'): raise Exception('Invalid path') - + # Check that the workflow name does not have characters that may cause issues if re.fullmatch(r'\w+', wf_name) is None: raise Exception('Invalid workflow name') - + result = subprocess.run(['reana-client', 'run', '-w', wf_name, '-f', yaml_file], cwd=workspace, capture_output=True) if result.returncode != 0: raise Exception(result.stderr.decode('utf-8')) - + self.finish(json.dumps({ 'status': 'success', 'message': 'Workflow created' @@ -227,6 +236,10 @@ def post(self): })) class WorkflowValidateHandler(APIHandler): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.client = ReanaAPIClient() + def post(self): try: body = json.loads(self.request.body) @@ -237,12 +250,12 @@ def post(self): if '..' in path or not os.path.isdir(workspace) or not yaml_file.endswith('.yaml'): raise Exception('Invalid path') - + result = subprocess.run(['reana-client', 'validate', '-f', yaml_file], cwd=workspace, capture_output=True) if result.returncode != 0: raise Exception(result.stderr.decode('utf-8')) - + self.finish(json.dumps({ 'status': 'success', 'message': result.stdout.decode('utf-8') @@ -252,4 +265,4 @@ def post(self): self.finish(json.dumps({ 'status': 'error', 'message': str(e) - })) \ No newline at end of file + })) diff --git a/reana_jupyterlab/server.py b/reana_jupyterlab/server.py index 4853e58..703e450 100644 --- a/reana_jupyterlab/server.py +++ b/reana_jupyterlab/server.py @@ -19,4 +19,4 @@ def _load_jupyter_server_extension(lab_app): # pragma: no cover JupyterLab application instance """ setup_handlers(lab_app) - lab_app.log.info("Registered Reana JupyterLab extension at URL path /reana_jupyterlab") \ No newline at end of file + lab_app.log.info("Registered Reana JupyterLab extension at URL path /reana_jupyterlab") diff --git a/src/components/@Connection/ConnectionForm.tsx b/src/components/@Connection/ConnectionForm.tsx deleted file mode 100644 index 0cb3240..0000000 --- a/src/components/@Connection/ConnectionForm.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import React from 'react'; -import { TextField } from '../TextField'; -import { createUseStyles } from 'react-jss'; - -import { IReanaAuthCredentials } from '../../types'; -import { Button } from '../Button'; -import { UIStore } from '../../stores/UIStore'; -import { requestAPI } from '../../utils/ApiRequest'; - -import { Notification } from '@jupyterlab/apputils'; - -const useStyles = createUseStyles({ - container: { - padding: '8px 16px 8px 16px' - }, - label: { - margin: '4px 0 4px 0' - }, - textFieldContainer: { - margin: '8px 0 8px 0' - }, - buttonsContainer: { - extend: 'textFieldContainer', - display: 'flex', - justifyContent: 'flex-end' - }, -}); - -interface IConnectionProps { - params?: IReanaAuthCredentials; - onAuthParamsChange: { (val: IReanaAuthCredentials): void }; - actionAfterSubmit?: { (): void }; -} - -type MyProps = IConnectionProps & React.HTMLAttributes; - -async function checkConnection(server: string, accessToken: string, actionAfterSubmit?: { (): void }) { - try { - const data = await requestAPI('env', { - method: 'POST', - body: JSON.stringify({ server, accessToken }), - headers: { - 'Content-Type': 'application/json' - } - }); - - Notification.emit( - data.message, - data.status, - { - autoClose: 3000, - } - ); - - // Update UI store - UIStore.update(s => { - s.authConfig = { - server: server, - accessToken: accessToken - }; - s.hasConnection = data.status === 'success'; - }); - - } catch (error) { - Notification.error( - 'Connection Error', - { - autoClose: 3000, - } - ) - } finally { - if (actionAfterSubmit) { - actionAfterSubmit(); - } - } - -} -export const ConnectionForm: React.FC = ({ - params = { server: '', accessToken: '' }, - onAuthParamsChange, - actionAfterSubmit = () => { } -}) => { - const classes = useStyles(); - - const onServerChange = (server: string) => { - onAuthParamsChange({ ...params, server }); - }; - - const onAccessTokenChange = (accessToken: string) => { - onAuthParamsChange({ ...params, accessToken }); - }; - - return ( - <> -
-
-
Server Name
- onServerChange(e.target.value)} - /> -
-
-
Access Token
- onAccessTokenChange(e.target.value)} - /> -
-
- -
-
- - ); -}; \ No newline at end of file diff --git a/src/components/MenuBar.tsx b/src/components/MenuBar.tsx index 29d78a9..c466379 100644 --- a/src/components/MenuBar.tsx +++ b/src/components/MenuBar.tsx @@ -62,7 +62,6 @@ export interface IMenu { title: any; value: any; right?: boolean; - disabled?: boolean; } export const MenuBar: React.FunctionComponent = ({ @@ -77,13 +76,12 @@ export const MenuBar: React.FunctionComponent = ({
    {menus.map(menu => { const activeClass = menu.value === value ? 'active' : ''; - const disabledClass = menu.disabled ? 'disabled' : ''; const tabClass = menu.right ? classes.tabItemRight : classes.tabItem; return (
  • onChange(menu.value) : undefined} + onClick={() => onChange(menu.value)} key={menu.value} - className={`${tabClass} ${activeClass} ${disabledClass}`} + className={`${tabClass} ${activeClass}`} > {menu.title}
  • @@ -92,4 +90,4 @@ export const MenuBar: React.FunctionComponent = ({
); -}; \ No newline at end of file +}; diff --git a/src/stores/UIStore.ts b/src/stores/UIStore.ts index 1fc90c5..5e63fe4 100644 --- a/src/stores/UIStore.ts +++ b/src/stores/UIStore.ts @@ -11,29 +11,15 @@ import { Store } from 'pullstate'; import { - IReanaAuthCredentials, IReanaWorkflow } from '../types'; export interface IUIState { - authConfig: IReanaAuthCredentials; hasConnection: boolean; selectedWorkflow: IReanaWorkflow | null; } -// export const initialState: IUIState = { -// authConfig: { -// server: '', -// accessToken: '' -// }, -// hasConnection: false -// }; - export const initialState: IUIState = { - authConfig: { - server: '', - accessToken: '' - }, hasConnection: false, selectedWorkflow: null }; @@ -42,11 +28,7 @@ export const UIStore = new Store(initialState); export const resetReanaCaches = (): void => { UIStore.update(s => { - s.authConfig = { - server: '', - accessToken: '' - }; s.hasConnection = false; s.selectedWorkflow = null; }); -}; \ No newline at end of file +}; diff --git a/src/types.ts b/src/types.ts index 9dd8d42..4526594 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,8 +1,3 @@ -export interface IReanaAuthCredentials { - server: string; - accessToken: string; -}; - export interface IReanaWorkflowStatus { id: string; name: string; @@ -51,9 +46,9 @@ export interface IReanaWorkflowSpecification { }; export interface IReanaWorkflow extends - IReanaWorkflowStatus, - Partial, - Partial, + IReanaWorkflowStatus, + Partial, + Partial, Partial { }; @@ -67,4 +62,4 @@ export interface IFileEntry { name: string; type: 'file' | 'directory'; path: string; -} \ No newline at end of file +} diff --git a/src/utils/ApiRequest.ts b/src/utils/ApiRequest.ts index 2910628..a0adab1 100644 --- a/src/utils/ApiRequest.ts +++ b/src/utils/ApiRequest.ts @@ -49,4 +49,4 @@ export async function requestAPI( } return convertSnakeCase ? camelcaseKeysDeep(data) : data; -} \ No newline at end of file +} diff --git a/src/widgets/SidebarPanel.tsx b/src/widgets/SidebarPanel.tsx index 78259ad..783267c 100644 --- a/src/widgets/SidebarPanel.tsx +++ b/src/widgets/SidebarPanel.tsx @@ -2,22 +2,16 @@ import { LabIcon } from '@jupyterlab/ui-components'; import { VDomRenderer } from '@jupyterlab/apputils'; import { createUseStyles } from 'react-jss'; -import React, { useState, useEffect } from 'react'; - +import React from 'react'; import reana_icon from '/src/images/reana-icon.svg'; import { Header } from '../components/Header'; import { MenuBar } from '../components/MenuBar'; -import { Loading } from '../components/Loading'; -import { ConnectionForm } from '../components/@Connection/ConnectionForm'; import { CreateForm } from '../components/@Create/CreateForm'; import { WorkflowList } from '../components/@Workflows/WorkflowsList'; -import { IReanaAuthCredentials, IReanaWorkflow, IReanaWorkflowStatus, IReanaCreateParams } from '../types'; -import { UIStore } from '../stores/UIStore'; -import { useStoreState } from 'pullstate'; +import { IReanaWorkflow, IReanaWorkflowStatus, IReanaCreateParams } from '../types'; import { HorizontalHeading } from '../components/HorizontalHeading'; -import { requestAPI } from '../utils/ApiRequest'; import { WorkflowDetails } from '../components/@Workflows/WorkflowDetails'; @@ -52,54 +46,16 @@ const useStyles = createUseStyles({ const Panel: React.FC = () => { const classes = useStyles(); - const [loading, setLoading] = useState(true); - useEffect(() => { - if (loading) { - const populateUIStore = async () => { - try{ - const data = await requestAPI('env', { - method: 'GET', - }); - - UIStore.update(s => { - s.authConfig = { - server: data.server, - accessToken: data.accessToken - }; - - s.hasConnection = !!data.server; - }); - - setAuthConfig(data); - - } catch (error) { - console.error('Error setting variables:', error); - } finally { - setLoading(false); - } - } - populateUIStore().catch(console.error); - }; - }, [loading]); - - const hasConnection = useStoreState(UIStore, s => s.hasConnection); - const [activeMenu, setActiveMenu] = React.useState(1); - const [authConfig, setAuthConfig] = React.useState(); const [workflows, setWorkflows] = React.useState([]); const [selectedWorkflow, setSelectedWorkflow] = React.useState(); const [creationParamsConfig, setCreationParamsConfig] = React.useState(); const menus = [ - { title: 'Connect', value: 1, right: false }, - { title: 'Workflows', value: 2, right: false, disabled: !hasConnection }, - { title: 'Create', value: 3, right: false, disabled: !hasConnection } + { title: 'Workflows', value: 1, right: false }, + { title: 'Create', value: 2, right: false } ]; - if (loading) { - return ; - } - return (
@@ -110,18 +66,6 @@ const Panel: React.FC = () => {
{activeMenu === 1 && (
- - {setAuthConfig(v)}} - actionAfterSubmit={() => setSelectedWorkflow(undefined)} - /> -
- )} -
-
- {activeMenu === 2 && ( -
{ selectedWorkflow !== undefined ? ( @@ -131,12 +75,12 @@ const Panel: React.FC = () => { setWorkflows={setWorkflows} setSelectedWorkflow={setSelectedWorkflow} /> - } -
+ } +
)}
-
- {activeMenu === 3 && ( +
+ {activeMenu === 2 && (
; } -} \ No newline at end of file +}