From 55135f9d63a708d980edf5dd8b4ddd46c8be20d7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=9D=92=E6=B9=9B?= <0x1304570@gmail.com>
Date: Mon, 9 Sep 2024 14:54:55 +1200
Subject: [PATCH] refactor: use redux to rewrite onboard (#8012)
---
config-ui/src/app/store.ts | 2 +
.../context.tsx => features/onboard/index.ts} | 32 +----
config-ui/src/features/onboard/slice.ts | 131 ++++++++++++++++++
config-ui/src/routes/app/app.tsx | 10 +-
config-ui/src/routes/app/loader.ts | 7 -
config-ui/src/routes/layout/layout.tsx | 10 +-
.../src/routes/onboard/components/card.tsx | 44 +++---
.../src/routes/onboard/components/tour.tsx | 8 +-
config-ui/src/routes/onboard/index.tsx | 108 +++++----------
config-ui/src/routes/onboard/step-0.tsx | 40 ++----
config-ui/src/routes/onboard/step-1.tsx | 27 ++--
config-ui/src/routes/onboard/step-2.tsx | 54 +++-----
config-ui/src/routes/onboard/step-3.tsx | 27 ++--
config-ui/src/routes/onboard/step-4.tsx | 52 ++-----
14 files changed, 264 insertions(+), 288 deletions(-)
rename config-ui/src/{routes/onboard/context.tsx => features/onboard/index.ts} (55%)
create mode 100644 config-ui/src/features/onboard/slice.ts
diff --git a/config-ui/src/app/store.ts b/config-ui/src/app/store.ts
index 8d2ce0c3429..8fec5046c6e 100644
--- a/config-ui/src/app/store.ts
+++ b/config-ui/src/app/store.ts
@@ -19,10 +19,12 @@
import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit';
import { connectionsSlice } from '@/features';
+import { onboardSlice } from '@/features/onboard';
export const store = configureStore({
reducer: {
connections: connectionsSlice.reducer,
+ onboard: onboardSlice.reducer,
},
});
diff --git a/config-ui/src/routes/onboard/context.tsx b/config-ui/src/features/onboard/index.ts
similarity index 55%
rename from config-ui/src/routes/onboard/context.tsx
rename to config-ui/src/features/onboard/index.ts
index e21d12d2ba0..513ab48a7f8 100644
--- a/config-ui/src/routes/onboard/context.tsx
+++ b/config-ui/src/features/onboard/index.ts
@@ -16,34 +16,4 @@
*
*/
-import { createContext } from 'react';
-
-export type Record = {
- plugin: string;
- connectionId: ID;
- blueprintId: ID;
- pipelineId: ID;
- scopeName: string;
-};
-
-const initialValue: {
- step: number;
- records: Record[];
- done: boolean;
- projectName?: string;
- plugin?: string;
- setStep: (value: number) => void;
- setRecords: (value: Record[]) => void;
- setProjectName: (value: string) => void;
- setPlugin: (value: string) => void;
-} = {
- step: 0,
- records: [],
- done: false,
- setStep: () => {},
- setRecords: () => {},
- setProjectName: () => {},
- setPlugin: () => {},
-};
-
-export const Context = createContext(initialValue);
+export * from './slice';
diff --git a/config-ui/src/features/onboard/slice.ts b/config-ui/src/features/onboard/slice.ts
new file mode 100644
index 00000000000..1ddebf57605
--- /dev/null
+++ b/config-ui/src/features/onboard/slice.ts
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
+
+import API from '@/api';
+import type { RootState } from '@/app/store';
+import type { IStatus } from '@/types';
+
+type DataType = {
+ initial: boolean;
+ step: number;
+ records: Array<{
+ plugin: string;
+ connectionId: ID;
+ blueprintId: ID;
+ pipelineId: ID;
+ scopeName: string;
+ }>;
+ projectName: string;
+ plugin: string;
+ done: boolean;
+};
+
+export const request = createAsyncThunk('onboard/request', async () => {
+ const res = await API.store.get('onboard');
+ return res;
+});
+
+export const update = createAsyncThunk('onboard/update', async (payload: Partial, { getState }) => {
+ const { data } = (getState() as RootState).onboard;
+ const res = await API.store.set('onboard', {
+ ...data,
+ ...payload,
+ step: payload.step ?? data.step + 1,
+ });
+ return res;
+});
+
+export const done = createAsyncThunk('onboard/done', async (_, { getState }) => {
+ const { data } = (getState() as RootState).onboard;
+ await API.store.set('onboard', {
+ ...data,
+ done: true,
+ });
+ return {};
+});
+
+const initialState: { status: IStatus; data: DataType } = {
+ status: 'idle',
+ data: {
+ initial: false,
+ step: 0,
+ records: [],
+ projectName: '',
+ plugin: '',
+ done: false,
+ },
+};
+
+export const onboardSlice = createSlice({
+ name: 'onboard',
+ initialState,
+ reducers: {
+ previous: (state) => {
+ state.data.step -= 1;
+ },
+ changeProjectName: (state, action) => {
+ state.data.projectName = action.payload;
+ },
+ changePlugin: (state, action) => {
+ state.data.plugin = action.payload;
+ },
+ changeRecords: (state, action) => {
+ state.data.records = action.payload;
+ },
+ },
+ extraReducers: (builder) => {
+ builder
+ .addCase(request.pending, (state) => {
+ state.status = 'loading';
+ })
+ .addCase(request.fulfilled, (state, action) => {
+ state.status = 'success';
+ state.data = {
+ ...action.payload,
+ initial: action.payload?.initial ?? false,
+ step: action.payload?.step ?? 0,
+ records: action.payload?.records ?? [],
+ done: action.payload?.done ?? false,
+ };
+ })
+ .addCase(update.fulfilled, (state, action) => {
+ state.data = {
+ ...state.data,
+ ...action.payload,
+ };
+ })
+ .addCase(done.fulfilled, (state) => {
+ state.data.done = true;
+ });
+ },
+});
+
+export default onboardSlice.reducer;
+
+export const { previous, changeProjectName, changePlugin, changeRecords } = onboardSlice.actions;
+
+export const selectStatus = (state: RootState) => state.onboard.status;
+
+export const selectOnboard = (state: RootState) => state.onboard.data;
+
+export const selectRecord = (state: RootState) => {
+ const { plugin, records } = state.onboard.data;
+ return records.find((it) => it.plugin === plugin);
+};
diff --git a/config-ui/src/routes/app/app.tsx b/config-ui/src/routes/app/app.tsx
index 72a6b75aa19..51c3ce0c78f 100644
--- a/config-ui/src/routes/app/app.tsx
+++ b/config-ui/src/routes/app/app.tsx
@@ -19,8 +19,10 @@
import { useEffect } from 'react';
import { useNavigate, useLoaderData, Outlet } from 'react-router-dom';
+import { PageLoading } from '@/components';
import { init } from '@/features';
-import { useAppDispatch } from '@/hooks';
+import { request as requestOnboard, selectStatus } from '@/features/onboard';
+import { useAppDispatch, useAppSelector } from '@/hooks';
import { setUpRequestInterceptor } from '@/utils';
export const App = () => {
@@ -29,11 +31,17 @@ export const App = () => {
const { version, plugins } = useLoaderData() as { version: string; plugins: string[] };
const dispatch = useAppDispatch();
+ const status = useAppSelector(selectStatus);
useEffect(() => {
setUpRequestInterceptor(navigate);
dispatch(init({ version, plugins }));
+ dispatch(requestOnboard());
}, []);
+ if (status === 'loading') {
+ return ;
+ }
+
return ;
};
diff --git a/config-ui/src/routes/app/loader.ts b/config-ui/src/routes/app/loader.ts
index 51d532f6e99..dad6d126738 100644
--- a/config-ui/src/routes/app/loader.ts
+++ b/config-ui/src/routes/app/loader.ts
@@ -16,7 +16,6 @@
*
*/
-import { redirect } from 'react-router-dom';
import { intersection } from 'lodash';
import API from '@/api';
@@ -27,12 +26,6 @@ type Props = {
};
export const appLoader = async ({ request }: Props) => {
- const onboard = await API.store.get('onboard');
-
- if (!onboard) {
- return redirect('/onboard');
- }
-
let fePlugins = getRegisterPlugins();
const bePlugins = await API.plugin.list();
diff --git a/config-ui/src/routes/layout/layout.tsx b/config-ui/src/routes/layout/layout.tsx
index 8e45dd96e4d..a67036a5c63 100644
--- a/config-ui/src/routes/layout/layout.tsx
+++ b/config-ui/src/routes/layout/layout.tsx
@@ -17,12 +17,13 @@
*/
import { useState, useEffect, useMemo } from 'react';
-import { Outlet, useNavigate, useLocation } from 'react-router-dom';
+import { Outlet, useNavigate, useLocation, Navigate } from 'react-router-dom';
import { Helmet } from 'react-helmet';
import { Layout as AntdLayout, Menu, Divider } from 'antd';
import { PageLoading, Logo, ExternalLink } from '@/components';
-import { selectError, selectStatus, selectVersion } from '@/features';
+import { selectError, selectStatus, selectVersion } from '@/features/connections';
+import { selectOnboard } from '@/features/onboard';
import { OnboardCard } from '@/routes/onboard/components';
import { useAppSelector } from '@/hooks';
@@ -39,6 +40,7 @@ export const Layout = () => {
const navigate = useNavigate();
const { pathname } = useLocation();
+ const { initial } = useAppSelector(selectOnboard);
const status = useAppSelector(selectStatus);
const error = useAppSelector(selectError);
const version = useAppSelector(selectVersion);
@@ -77,6 +79,10 @@ export const Layout = () => {
throw error.message;
}
+ if (!initial) {
+ return ;
+ }
+
return (
diff --git a/config-ui/src/routes/onboard/components/card.tsx b/config-ui/src/routes/onboard/components/card.tsx
index 3c59ee082d2..b73786a291d 100644
--- a/config-ui/src/routes/onboard/components/card.tsx
+++ b/config-ui/src/routes/onboard/components/card.tsx
@@ -16,14 +16,14 @@
*
*/
-import { useState, useMemo } from 'react';
+import { useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
import { CloseOutlined, LoadingOutlined, CheckCircleFilled, CloseCircleFilled } from '@ant-design/icons';
import { theme, Card, Flex, Progress, Space, Button, Modal } from 'antd';
import API from '@/api';
-import { useRefreshData, useAutoRefresh } from '@/hooks';
-import { operator } from '@/utils';
+import { selectOnboard, selectRecord, done as doneFuc } from '@/features/onboard';
+import { useAppDispatch, useAppSelector, useAutoRefresh } from '@/hooks';
import { DashboardURLMap } from '../step-4';
@@ -32,24 +32,21 @@ interface Props {
}
export const OnboardCard = ({ style }: Props) => {
- const [oeprating, setOperating] = useState(false);
- const [version, setVersion] = useState(0);
-
const navigate = useNavigate();
+ const dispatch = useAppDispatch();
+ const { step, plugin, done } = useAppSelector(selectOnboard);
+ const record = useAppSelector(selectRecord);
+
const {
token: { green5, orange5, red5 },
} = theme.useToken();
const [modal, contextHolder] = Modal.useModal();
- const { ready, data } = useRefreshData(() => API.store.get('onboard'), [version]);
-
- const record = useMemo(() => (data ? data.records.find((it: any) => it.plugin === data.plugin) : null), [data]);
-
const tasksRes = useAutoRefresh(
async () => {
- if ((data && data.done) || !record) {
+ if (done || !record) {
return;
}
@@ -64,7 +61,7 @@ export const OnboardCard = ({ style }: Props) => {
);
const status = useMemo(() => {
- if (!data || data.step !== 4) {
+ if (step !== 4) {
return 'prepare';
}
@@ -83,30 +80,21 @@ export const OnboardCard = ({ style }: Props) => {
default:
return 'running';
}
- }, [data, tasksRes]);
+ }, [step, tasksRes]);
const handleClose = async () => {
modal.confirm({
width: 600,
title: 'Permanently close this entry?',
content: 'You will not be able to get back to the onboarding session again.',
- okButtonProps: {
- loading: oeprating,
- },
okText: 'Confirm',
- onOk: async () => {
- const [success] = await operator(() => API.store.set('onboard', { ...data, done: true }), {
- setOperating,
- });
-
- if (success) {
- setVersion(version + 1);
- }
+ onOk() {
+ dispatch(doneFuc());
},
});
};
- if (!ready || !data || data.done) {
+ if (done) {
return null;
}
@@ -115,7 +103,7 @@ export const OnboardCard = ({ style }: Props) => {
{status === 'prepare' && (
-
diff --git a/config-ui/src/routes/onboard/components/tour.tsx b/config-ui/src/routes/onboard/components/tour.tsx
index 875ce2e1857..bac65ce67e1 100644
--- a/config-ui/src/routes/onboard/components/tour.tsx
+++ b/config-ui/src/routes/onboard/components/tour.tsx
@@ -18,8 +18,8 @@
import { Tour } from 'antd';
-import API from '@/api';
-import { useRefreshData } from '@/hooks';
+import { selectOnboard } from '@/features/onboard';
+import { useAppSelector } from '@/hooks';
interface Props {
nameRef: React.RefObject;
@@ -28,7 +28,7 @@ interface Props {
}
export const OnboardTour = ({ nameRef, connectionRef, configRef }: Props) => {
- const { ready, data } = useRefreshData(() => API.store.get('onboard'), []);
+ const { step, done } = useAppSelector(selectOnboard);
const steps = [
{
@@ -49,7 +49,7 @@ export const OnboardTour = ({ nameRef, connectionRef, configRef }: Props) => {
},
];
- if (!ready || !data || data.step !== 4 || data.done) {
+ if (step !== 4 || done) {
return null;
}
diff --git a/config-ui/src/routes/onboard/index.tsx b/config-ui/src/routes/onboard/index.tsx
index 061be20331f..239a4b973e8 100644
--- a/config-ui/src/routes/onboard/index.tsx
+++ b/config-ui/src/routes/onboard/index.tsx
@@ -16,19 +16,15 @@
*
*/
-import React, { useState, useEffect } from 'react';
-import { useNavigate } from 'react-router-dom';
+import { useNavigate, Navigate } from 'react-router-dom';
import { Helmet } from 'react-helmet';
import { CloseOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
import { theme, Layout, Modal } from 'antd';
-import API from '@/api';
-import { PageLoading } from '@/components';
+import { selectOnboard } from '@/features/onboard';
import { PATHS } from '@/config';
-import { useRefreshData } from '@/hooks';
+import { useAppSelector } from '@/hooks';
-import type { Record } from './context';
-import { Context } from './context';
import { Step0 } from './step-0';
import { Step1 } from './step-1';
import { Step2 } from './step-2';
@@ -59,30 +55,16 @@ interface Props {
}
export const Onboard = ({ logo, title }: Props) => {
- const [step, setStep] = useState(0);
- const [records, setRecords] = useState([]);
- const [projectName, setProjectName] = useState();
- const [plugin, setPlugin] = useState();
-
const navigate = useNavigate();
+ const { step, done } = useAppSelector(selectOnboard);
+
const {
token: { colorPrimary },
} = theme.useToken();
const [modal, contextHolder] = Modal.useModal();
- const { ready, data } = useRefreshData(() => API.store.get('onboard'));
-
- useEffect(() => {
- if (ready && data) {
- setStep(data.step);
- setRecords(data.records);
- setProjectName(data.projectName);
- setPlugin(data.plugin);
- }
- }, [ready, data]);
-
const handleClose = () => {
modal.confirm({
width: 820,
@@ -94,58 +76,44 @@ export const Onboard = ({ logo, title }: Props) => {
});
};
- if (!ready) {
- return ;
+ if (done) {
+ return ;
}
return (
-
+
Onboard - {brandName}
-
-
- {step === 0 ? (
-
- ) : (
- <>
-
- Connect to your first repository
-
-
-
- {[1, 2, 3].includes(step) && (
-
- {steps.map((it) => (
-
- {it.step}
- {it.title}
-
- ))}
-
- )}
- {step === 1 && }
- {step === 2 && }
- {step === 3 && }
- {step === 4 && }
-
- >
- )}
-
- {contextHolder}
-
-
+
+ {step === 0 ? (
+
+ ) : (
+ <>
+
+ Connect to your first repository
+
+
+
+ {[1, 2, 3].includes(step) && (
+
+ {steps.map((it) => (
+
+ {it.step}
+ {it.title}
+
+ ))}
+
+ )}
+ {step === 1 && }
+ {step === 2 && }
+ {step === 3 && }
+ {step === 4 && }
+
+ >
+ )}
+
+ {contextHolder}
+
);
};
diff --git a/config-ui/src/routes/onboard/step-0.tsx b/config-ui/src/routes/onboard/step-0.tsx
index 5e504bd42db..c015515f2b5 100644
--- a/config-ui/src/routes/onboard/step-0.tsx
+++ b/config-ui/src/routes/onboard/step-0.tsx
@@ -16,19 +16,17 @@
*
*/
-import { useState, useContext } from 'react';
import { useNavigate } from 'react-router-dom';
import { ExclamationCircleOutlined, CloseOutlined } from '@ant-design/icons';
import { Modal, Flex, Button } from 'antd';
import styled from 'styled-components';
-import API from '@/api';
import { Logo } from '@/components';
import { PATHS } from '@/config';
+import { update } from '@/features/onboard';
+import { useAppDispatch } from '@/hooks';
import { operator } from '@/utils';
-import { Context } from './context';
-
const Wrapper = styled.div`
.logo {
display: flex;
@@ -65,14 +63,11 @@ interface Props {
}
export const Step0 = ({ logo = , title = 'DevLake' }: Props) => {
- const [operating, setOperating] = useState(false);
-
const navigate = useNavigate();
+ const dispatch = useAppDispatch();
const [modal, contextHolder] = Modal.useModal();
- const { step, records, done, projectName, plugin, setStep } = useContext(Context);
-
const handleClose = () => {
modal.confirm({
width: 820,
@@ -80,15 +75,10 @@ export const Step0 = ({ logo = , title = 'DevLake
content: 'You can get back to this session via the card on top of the Projects page.',
icon: ,
okText: 'Confirm',
- onOk: async () => {
- const [success] = await operator(
- () => API.store.set('onboard', { step: 0, records, done, projectName, plugin }),
- {
- setOperating,
- hideToast: true,
- },
- );
-
+ async onOk() {
+ const [success] = await operator(() => dispatch(update({ initial: true, step: 0 })).unwrap(), {
+ hideToast: true,
+ });
if (success) {
navigate(PATHS.ROOT());
}
@@ -96,20 +86,6 @@ export const Step0 = ({ logo = , title = 'DevLake
});
};
- const handleSubmit = async () => {
- const [success] = await operator(
- async () => API.store.set('onboard', { step: 1, records, done, projectName, plugin }),
- {
- setOperating,
- hideToast: true,
- },
- );
-
- if (success) {
- setStep(step + 1);
- }
- };
-
return (
{contextHolder}
@@ -123,7 +99,7 @@ export const Step0 = ({ logo = , title = 'DevLake
With just a few clicks, you can integrate your initial DevOps tool and observe engineering metrics.
-
+ dispatch(update({}))}>
Connect to your first repository
diff --git a/config-ui/src/routes/onboard/step-1.tsx b/config-ui/src/routes/onboard/step-1.tsx
index a6c16632aa8..a6582c4469d 100644
--- a/config-ui/src/routes/onboard/step-1.tsx
+++ b/config-ui/src/routes/onboard/step-1.tsx
@@ -16,24 +16,26 @@
*
*/
-import { useState, useEffect, useContext } from 'react';
+import { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { Input, Flex, Button, message } from 'antd';
import API from '@/api';
import { Block, Markdown } from '@/components';
import { PATHS } from '@/config';
+import { selectOnboard, update, previous, changeProjectName, changePlugin } from '@/features/onboard';
+import { useAppDispatch, useAppSelector } from '@/hooks';
import { ConnectionSelect } from '@/plugins';
import { operator } from '@/utils';
-import { Context } from './context';
import * as S from './styled';
export const Step1 = () => {
const [QA, setQA] = useState('');
const [operating, setOperating] = useState(false);
- const { step, records, done, projectName, plugin, setStep, setProjectName, setPlugin } = useContext(Context);
+ const dispatch = useAppDispatch();
+ const { projectName, plugin } = useAppSelector(selectOnboard);
useEffect(() => {
fetch(`/onboard/step-1/${plugin ? plugin : 'default'}.md`)
@@ -56,14 +58,7 @@ export const Step1 = () => {
return;
}
- const [success] = await operator(() => API.store.set('onboard', { step: 2, records, done, projectName, plugin }), {
- setOperating,
- hideToast: true,
- });
-
- if (success) {
- setStep(step + 1);
- }
+ dispatch(update({}));
};
return (
@@ -79,16 +74,16 @@ export const Step1 = () => {
style={{ width: 386 }}
placeholder="Your Project Name"
value={projectName}
- onChange={(e) => setProjectName(e.target.value)}
+ onChange={(e) => dispatch(changeProjectName(e.target.value))}
/>
+ <>
For self-managed GitLab/GitHub/Bitbucket, please skip the onboarding and configure via{' '}
Data Connections.
-
+ >
}
required
>
@@ -117,14 +112,14 @@ export const Step1 = () => {
},
]}
value={plugin}
- onChange={setPlugin}
+ onChange={(p) => dispatch(changePlugin(p))}
/>
{QA}
- setStep(step - 1)}>
+ dispatch(previous())}>
Previous Step
diff --git a/config-ui/src/routes/onboard/step-2.tsx b/config-ui/src/routes/onboard/step-2.tsx
index cd2eba4dccd..c306e026c93 100644
--- a/config-ui/src/routes/onboard/step-2.tsx
+++ b/config-ui/src/routes/onboard/step-2.tsx
@@ -16,20 +16,21 @@
*
*/
-import { useState, useContext, useEffect, useMemo } from 'react';
+import { useState, useEffect, useMemo } from 'react';
import { Link } from 'react-router-dom';
import { Flex, Button, Tooltip } from 'antd';
import API from '@/api';
import { Markdown } from '@/components';
import { PATHS } from '@/config';
+import { selectOnboard, previous, update } from '@/features/onboard';
+import { useAppDispatch, useAppSelector } from '@/hooks';
import { getPluginConfig } from '@/plugins';
import { ConnectionToken } from '@/plugins/components/connection-form/fields/token';
import { ConnectionUsername } from '@/plugins/components/connection-form/fields/username';
import { ConnectionPassword } from '@/plugins/components/connection-form/fields/password';
import { operator } from '@/utils';
-import { Context } from './context';
import * as S from './styled';
const paramsMap: Record = {
@@ -48,12 +49,12 @@ const paramsMap: Record = {
export const Step2 = () => {
const [QA, setQA] = useState('');
- const [operating, setOperating] = useState(false);
const [testing, setTesting] = useState(false);
const [testStaus, setTestStatus] = useState(false);
const [payload, setPayload] = useState({});
- const { step, records, done, projectName, plugin, setStep, setRecords } = useContext(Context);
+ const dispatch = useAppDispatch();
+ const { plugin, records } = useAppSelector(selectOnboard);
const config = useMemo(() => getPluginConfig(plugin as string), [plugin]);
@@ -91,38 +92,17 @@ export const Step2 = () => {
return;
}
- const [success] = await operator(
- async () => {
- const connection = await API.connection.create(plugin, {
- name: `${plugin}-${Date.now()}`,
- ...paramsMap[plugin],
- ...payload,
- });
-
- const newRecords = [
- ...records,
- { plugin, connectionId: connection.id, blueprintId: '', pipelineId: '', scopeName: '' },
- ];
-
- setRecords(newRecords);
-
- await API.store.set('onboard', {
- step: 3,
- records: newRecords,
- done,
- projectName,
- plugin,
- });
- },
- {
- setOperating,
- hideToast: true,
- },
- );
+ const connection = await API.connection.create(plugin, {
+ name: `${plugin}-${Date.now()}`,
+ ...paramsMap[plugin],
+ ...payload,
+ });
- if (success) {
- setStep(step + 1);
- }
+ dispatch(
+ update({
+ records: [...records, { plugin, connectionId: connection.id, blueprintId: '', pipelineId: '', scopeName: '' }],
+ }),
+ );
};
if (!plugin) {
@@ -205,10 +185,10 @@ export const Step2 = () => {
{QA}
- setStep(step - 1)}>
+ dispatch(previous())}>
Previous Step
-
+
Next Step
diff --git a/config-ui/src/routes/onboard/step-3.tsx b/config-ui/src/routes/onboard/step-3.tsx
index 4ee900bfac9..58dcf85e9de 100644
--- a/config-ui/src/routes/onboard/step-3.tsx
+++ b/config-ui/src/routes/onboard/step-3.tsx
@@ -16,16 +16,17 @@
*
*/
-import { useState, useContext, useEffect, useMemo } from 'react';
+import { useState, useEffect, useMemo } from 'react';
import { Flex, Button } from 'antd';
import dayjs from 'dayjs';
import API from '@/api';
import { Markdown } from '@/components';
+import { selectOnboard, previous, update } from '@/features/onboard';
+import { useAppDispatch, useAppSelector } from '@/hooks';
import { DataScopeRemote, getPluginScopeId } from '@/plugins';
import { operator, formatTime } from '@/utils';
-import { Context } from './context';
import * as S from './styled';
export const Step3 = () => {
@@ -33,7 +34,8 @@ export const Step3 = () => {
const [operating, setOperating] = useState(false);
const [scopes, setScopes] = useState([]);
- const { step, records, done, projectName, plugin, setStep, setRecords } = useContext(Context);
+ const dispatch = useAppDispatch();
+ const { projectName, plugin, records } = useAppSelector(selectOnboard);
useEffect(() => {
fetch(`/onboard/step-3/${plugin}.md`)
@@ -51,7 +53,7 @@ export const Step3 = () => {
return;
}
- const [success] = await operator(
+ const [success, res] = await operator(
async () => {
// 1. create a new project
const { blueprint } = await API.project.create({
@@ -89,7 +91,7 @@ export const Step3 = () => {
// 5. get current run pipeline
const pipeline = await API.blueprint.pipelines(blueprint.id);
- const newRecords = records.map((it) =>
+ return records.map((it) =>
it.plugin !== plugin
? it
: {
@@ -99,17 +101,6 @@ export const Step3 = () => {
scopeName: scopes[0]?.fullName ?? scopes[0].name,
},
);
-
- setRecords(newRecords);
-
- // 6. update store
- await API.store.set('onboard', {
- step: 4,
- records: newRecords,
- done,
- projectName,
- plugin,
- });
},
{
setOperating,
@@ -118,7 +109,7 @@ export const Step3 = () => {
);
if (success) {
- setStep(step + 1);
+ dispatch(update({ records: res }));
}
};
@@ -142,7 +133,7 @@ export const Step3 = () => {
{QA}
- setStep(step - 1)}>
+ dispatch(previous())}>
Previous Step
diff --git a/config-ui/src/routes/onboard/step-4.tsx b/config-ui/src/routes/onboard/step-4.tsx
index eb1be96880c..332e08b6b3e 100644
--- a/config-ui/src/routes/onboard/step-4.tsx
+++ b/config-ui/src/routes/onboard/step-4.tsx
@@ -16,19 +16,19 @@
*
*/
-import { useState, useContext, useMemo } from 'react';
-import { useNavigate } from 'react-router-dom';
+import { useState, useMemo } from 'react';
import { CheckCircleOutlined, CloseCircleOutlined } from '@ant-design/icons';
import { theme, Progress, Space, Button } from 'antd';
import styled from 'styled-components';
import API from '@/api';
import { ExternalLink } from '@/components';
+import { selectOnboard, selectRecord, update } from '@/features/onboard';
+import { useAppDispatch, useAppSelector } from '@/hooks';
import { useAutoRefresh } from '@/hooks';
import { operator } from '@/utils';
import { Logs } from './components';
-import { Context } from './context';
const Wrapper = styled.div`
margin-top: 150px;
@@ -103,11 +103,9 @@ const getStatus = (data: any) => {
export const Step4 = () => {
const [operating, setOperating] = useState(false);
- const navigate = useNavigate();
-
- const { step, records, done, projectName, plugin, setRecords } = useContext(Context);
-
- const record = useMemo(() => records.find((it) => it.plugin === plugin), [plugin, records]);
+ const dispatch = useAppDispatch();
+ const { plugin, records } = useAppSelector(selectOnboard);
+ const record = useAppSelector(selectRecord);
const { data } = useAutoRefresh(
async () => {
@@ -171,32 +169,12 @@ export const Step4 = () => {
token: { green5, orange5, red5 },
} = theme.useToken();
- const handleFinish = async () => {
- const [success] = await operator(
- () =>
- API.store.set('onboard', {
- step,
- records,
- done: true,
- projectName,
- plugin,
- }),
- {
- setOperating,
- },
- );
-
- if (success) {
- navigate('/');
- }
- };
-
const handleRecollectData = async () => {
if (!record) {
return null;
}
- const [success] = await operator(
+ const [success, res] = await operator(
async () => {
// 1. re trigger this bulueprint
await API.blueprint.trigger(record.blueprintId, { skipCollectors: false, fullSync: false });
@@ -204,7 +182,7 @@ export const Step4 = () => {
// 2. get current run pipeline
const pipeline = await API.blueprint.pipelines(record.blueprintId);
- const newRecords = records.map((it) =>
+ return records.map((it) =>
it.plugin !== plugin
? it
: {
@@ -212,17 +190,6 @@ export const Step4 = () => {
pipelineId: pipeline.pipelines[0].id,
},
);
-
- setRecords(newRecords);
-
- // 3. update store
- await API.store.set('onboard', {
- step: 4,
- records: newRecords,
- done,
- projectName,
- plugin,
- });
},
{
setOperating,
@@ -230,6 +197,7 @@ export const Step4 = () => {
);
if (success) {
+ dispatch(update({ step: 4, records: res }));
}
};
@@ -261,7 +229,7 @@ export const Step4 = () => {
window.open(DashboardURLMap[plugin])}>
Check Dashboard
-
+ dispatch(update({ step: 4, done: true }))}>
Finish and Exit