diff --git a/config-ui/src/App.tsx b/config-ui/src/App.tsx
index 11757250e680..433c7a16fd81 100644
--- a/config-ui/src/App.tsx
+++ b/config-ui/src/App.tsx
@@ -16,8 +16,11 @@
*
*/
+import { useEffect } from 'react';
import { createBrowserRouter, Navigate, RouterProvider, json } from 'react-router-dom';
+import { useAppDispatch, useAppSelector } from '@/app/hook';
+import { init, selectStatus } from '@/features';
import { PageLoading } from '@/components';
import {
ConnectionHomePage,
@@ -100,4 +103,17 @@ const router = createBrowserRouter([
},
]);
-export const App = () => } />;
+export const App = () => {
+ const dispatch = useAppDispatch();
+ const status = useAppSelector(selectStatus);
+
+ useEffect(() => {
+ dispatch(init());
+ }, []);
+
+ if (['idle', 'loading'].includes(status)) {
+ return ;
+ }
+
+ return } />;
+};
diff --git a/config-ui/src/features/connections/slice.ts b/config-ui/src/features/connections/slice.ts
index 66b534d1f617..f0494f929aaf 100644
--- a/config-ui/src/features/connections/slice.ts
+++ b/config-ui/src/features/connections/slice.ts
@@ -20,7 +20,6 @@ import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import { flatten } from 'lodash';
import API from '@/api';
-import type { ConnectionForm } from '@/api/connection/types';
import { RootState } from '@/app/store';
import { PluginConfig } from '@/plugins';
import { IConnection, IConnectionStatus } from '@/types';
@@ -29,8 +28,10 @@ import { transformConnection } from './utils';
const initialState: {
connections: IConnection[];
+ status: 'idle' | 'loading' | 'success' | 'failed';
} = {
connections: [],
+ status: 'idle',
};
export const init = createAsyncThunk('connections/init', async () => {
@@ -77,7 +78,21 @@ export const addConnection = createAsyncThunk('connections/addConnection', async
return transformConnection(plugin, connection);
});
-export const updateConnection = createAsyncThunk('connections/updateConnection', async (payload: ConnectionForm) => {});
+export const updateConnection = createAsyncThunk(
+ 'connections/updateConnection',
+ async ({ plugin, connectionId, ...payload }: any) => {
+ const connection = await API.connection.update(plugin, connectionId, payload);
+ return transformConnection(plugin, connection);
+ },
+);
+
+export const removeConnection = createAsyncThunk(
+ 'connections/removeConnection',
+ async ({ plugin, connectionId }: any) => {
+ await API.connection.remove(plugin, connectionId);
+ return `${plugin}-${connectionId}`;
+ },
+);
export const slice = createSlice({
name: 'connections',
@@ -85,8 +100,12 @@ export const slice = createSlice({
reducers: {},
extraReducers(builder) {
builder
+ .addCase(init.pending, (state) => {
+ state.status = 'loading';
+ })
.addCase(init.fulfilled, (state, action) => {
state.connections = action.payload;
+ state.status = 'success';
})
.addCase(fetchConnections.fulfilled, (state, action) => {
state.connections = state.connections.concat(action.payload.connections);
@@ -94,6 +113,17 @@ export const slice = createSlice({
.addCase(addConnection.fulfilled, (state, action) => {
state.connections.push(action.payload);
})
+ .addCase(updateConnection.fulfilled, (state, action) => {
+ state.connections = state.connections.map((cs) => {
+ if (cs.unique === action.payload.unique) {
+ return action.payload;
+ }
+ return cs;
+ });
+ })
+ .addCase(removeConnection.fulfilled, (state, action) => {
+ state.connections = state.connections.filter((cs) => cs.unique !== action.payload);
+ })
.addCase(testConnection.pending, (state, action) => {
const existingConnection = state.connections.find((cs) => cs.unique === action.meta.arg.unique);
if (existingConnection) {
@@ -113,6 +143,8 @@ export const {} = slice.actions;
export default slice.reducer;
+export const selectStatus = (state: RootState) => state.connections.status;
+
export const selectAllConnections = (state: RootState) => state.connections.connections;
export const selectConnections = (state: RootState, plugin: string) =>
diff --git a/config-ui/src/pages/connection/detail/index.tsx b/config-ui/src/pages/connection/detail/index.tsx
index 2ce5e207ed31..e040c82ca367 100644
--- a/config-ui/src/pages/connection/detail/index.tsx
+++ b/config-ui/src/pages/connection/detail/index.tsx
@@ -21,9 +21,9 @@ import { useParams, useNavigate, Link } from 'react-router-dom';
import { Button, Intent } from '@blueprintjs/core';
import API from '@/api';
-import { useAppSelector } from '@/app/hook';
+import { useAppDispatch, useAppSelector } from '@/app/hook';
import { PageHeader, Buttons, Dialog, IconButton, Table, Message, toast } from '@/components';
-import { selectConnection } from '@/features';
+import { selectConnection, removeConnection } from '@/features';
import { useTips, useRefreshData } from '@/hooks';
import ClearImg from '@/images/icons/clear.svg';
import {
@@ -64,7 +64,9 @@ export const ConnectionDetailPage = () => {
const { plugin, id } = useParams() as { plugin: string; id: string };
const connectionId = +id;
+ const dispatch = useAppDispatch();
const connection = useAppSelector((state) => selectConnection(state, `${plugin}-${connectionId}`)) as IConnection;
+
const navigate = useNavigate();
const { setTips } = useTips();
const { ready, data } = useRefreshData(
@@ -102,7 +104,7 @@ export const ConnectionDetailPage = () => {
const [, res] = await operator(
async () => {
try {
- await API.connection.remove(plugin, connectionId);
+ await dispatch(removeConnection({ plugin, connectionId }));
return { status: 'success' };
} catch (err: any) {
const { status, data } = err.response;
diff --git a/config-ui/src/plugins/components/connection-form/index.tsx b/config-ui/src/plugins/components/connection-form/index.tsx
index cb7ffa549153..1a6d93a65265 100644
--- a/config-ui/src/plugins/components/connection-form/index.tsx
+++ b/config-ui/src/plugins/components/connection-form/index.tsx
@@ -23,6 +23,7 @@ import { pick } from 'lodash';
import API from '@/api';
import { useAppDispatch, useAppSelector } from '@/app/hook';
import { ExternalLink, Buttons } from '@/components';
+import { addConnection, updateConnection } from '@/features';
import { selectConnection } from '@/features/connections';
import { PluginConfig, PluginConfigType } from '@/plugins';
import { operator } from '@/utils';
@@ -82,7 +83,9 @@ export const ConnectionForm = ({ plugin, connectionId, onSuccess }: Props) => {
const handleSave = async () => {
const [success, res] = await operator(
() =>
- !connectionId ? API.connection.create(plugin, values) : API.connection.update(plugin, connectionId, values),
+ !connectionId
+ ? dispatch(addConnection({ plugin, ...values })).unwrap()
+ : dispatch(updateConnection({ plugin, connectionId, ...values })).unwrap(),
{
setOperating,
formatMessage: () => (!connectionId ? 'Create a New Connection Successful.' : 'Update Connection Successful.'),
diff --git a/config-ui/src/routes/layout/layout.tsx b/config-ui/src/routes/layout/layout.tsx
index cc5b4b260e45..de136ae4d404 100644
--- a/config-ui/src/routes/layout/layout.tsx
+++ b/config-ui/src/routes/layout/layout.tsx
@@ -16,14 +16,12 @@
*
*/
-import { useEffect, useRef } from 'react';
+import { useRef } from 'react';
import { useLoaderData, Outlet, useNavigate, useLocation } from 'react-router-dom';
import { CSSTransition } from 'react-transition-group';
import { Menu, MenuItem, Navbar, Alignment } from '@blueprintjs/core';
-import { useAppDispatch } from '@/app/hook';
import { Logo, ExternalLink, IconButton } from '@/components';
-import { init } from '@/features';
import { DOC_URL } from '@/release';
import { TipsContextProvider, TipsContextConsumer } from '@/store';
@@ -41,12 +39,6 @@ import './tips-transition.css';
export const Layout = () => {
const { version } = useLoaderData() as Awaited>;
- const dispatch = useAppDispatch();
-
- useEffect(() => {
- dispatch(init());
- }, []);
-
const navigate = useNavigate();
const { pathname } = useLocation();