This is an automated email from the ASF dual-hosted git repository.
likyh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git
The following commit(s) were added to refs/heads/main by this push:
new 8718e09e6 feat(config-ui): use new page connection home and detail
(#5149)
8718e09e6 is described below
commit 8718e09e67bdd5238cfe181615f4260fe6bb85a5
Author: 青湛 <[email protected]>
AuthorDate: Thu May 11 09:43:06 2023 +0800
feat(config-ui): use new page connection home and detail (#5149)
* refactor(config-ui): adjust the style for dialog, table and page-header"
* refactor(config-ui): the connection store
* refactor(config-ui): the connection form plugin
* fix(config-ui): format the misc in transformation form
* refactor(config-ui): remove the connection submenu
* feat(config-ui): use new page connection home and detail
---
config-ui/src/App.tsx | 9 +-
config-ui/src/components/dialog/styled.ts | 4 +-
config-ui/src/components/page-header/styled.ts | 4 +-
config-ui/src/components/table/styled.ts | 8 +-
config-ui/src/layouts/base/use-menu.ts | 9 -
.../pages/blueprint/create/components/step-1.tsx | 4 +-
.../src/pages/connection/{list => detail}/api.ts | 0
config-ui/src/pages/connection/detail/index.tsx | 152 +++++++++++++++
.../pages/connection/{list => detail}/styled.ts | 39 ++--
config-ui/src/pages/connection/form/index.tsx | 44 -----
config-ui/src/pages/connection/home/count.tsx | 27 ---
config-ui/src/pages/connection/home/index.tsx | 212 +++++++++++++++------
config-ui/src/pages/connection/home/styled.ts | 10 +
config-ui/src/pages/connection/index.ts | 3 +-
config-ui/src/pages/connection/list/connection.tsx | 142 --------------
config-ui/src/pages/connection/list/index.tsx | 46 -----
.../plugins/components/connection-form/index.tsx | 65 ++++++-
.../components/connection-form/operate/index.ts | 20 --
.../components/connection-form/operate/save.tsx | 65 -------
.../components/connection-form/operate/test.tsx | 65 -------
.../plugins/components/connection-form/styled.ts | 7 +-
.../plugins/components/transformation-form/misc.ts | 12 +-
config-ui/src/plugins/config.ts | 4 +-
config-ui/src/plugins/register/ae/config.ts | 2 +-
config-ui/src/plugins/register/azure/config.tsx | 7 +-
.../src/plugins/register/bitbucket/config.tsx | 4 +-
config-ui/src/plugins/register/customize/config.ts | 2 +-
config-ui/src/plugins/register/dbt/config.ts | 2 +-
config-ui/src/plugins/register/dora/config.ts | 2 +-
config-ui/src/plugins/register/feishu/config.ts | 2 +-
config-ui/src/plugins/register/gitee/config.ts | 2 +-
.../src/plugins/register/gitextractor/config.ts | 2 +-
config-ui/src/plugins/register/github/config.tsx | 6 +-
.../src/plugins/register/github_graphql/config.ts | 2 +-
config-ui/src/plugins/register/gitlab/config.tsx | 6 +-
config-ui/src/plugins/register/jenkins/config.ts | 4 +-
config-ui/src/plugins/register/jira/config.tsx | 4 +-
config-ui/src/plugins/register/org/config.ts | 2 +-
.../src/plugins/register/pagerduty/config.tsx | 4 +-
config-ui/src/plugins/register/refdiff/config.ts | 2 +-
config-ui/src/plugins/register/sonarqube/config.ts | 4 +-
config-ui/src/plugins/register/starrocks/config.ts | 2 +-
config-ui/src/plugins/register/tapd/config.tsx | 7 +-
.../src/plugins/register/teambition/config.tsx | 6 +-
config-ui/src/plugins/register/webook/config.ts | 6 +-
config-ui/src/plugins/register/zentao/config.ts | 4 +-
config-ui/src/store/connections/api.ts | 13 +-
config-ui/src/store/connections/context.tsx | 4 +-
config-ui/src/store/connections/status.tsx | 47 ++---
.../src/store/connections/use-context-value.ts | 179 ++++++++---------
50 files changed, 586 insertions(+), 692 deletions(-)
diff --git a/config-ui/src/App.tsx b/config-ui/src/App.tsx
index 1f24e9b27..51b14cb60 100644
--- a/config-ui/src/App.tsx
+++ b/config-ui/src/App.tsx
@@ -24,11 +24,10 @@ import { FromEnum } from '@/pages';
import {
OfflinePage,
DBMigratePage,
+ ConnectionHomePage,
+ ConnectionDetailPage,
ProjectHomePage,
ProjectDetailPage,
- ConnectionHomePage,
- ConnectionListPage,
- ConnectionFormPage,
BlueprintHomePage,
BlueprintCreatePage,
BlueprintDetailPage,
@@ -69,9 +68,7 @@ function App() {
<Switch>
<Route exact path="/" component={() => <Redirect
to="/connections" />} />
<Route exact path="/connections" component={() =>
<ConnectionHomePage />} />
- <Route exact path="/connections/:plugin" component={() =>
<ConnectionListPage />} />
- <Route exact path="/connections/:plugin/create" component={()
=> <ConnectionFormPage />} />
- <Route exact path="/connections/:plugin/:cid" component={() =>
<ConnectionFormPage />} />
+ <Route exact path="/connections/:plugin/:id" component={() =>
<ConnectionDetailPage />} />
<Route exact path="/projects" component={() =>
<ProjectHomePage />} />
<Route exact path="/projects/:pname" component={() =>
<ProjectDetailPage />} />
<Route
diff --git a/config-ui/src/components/dialog/styled.ts
b/config-ui/src/components/dialog/styled.ts
index 454fe0457..500401556 100644
--- a/config-ui/src/components/dialog/styled.ts
+++ b/config-ui/src/components/dialog/styled.ts
@@ -45,6 +45,8 @@ export const Header = styled.div`
}
`;
-export const Body = styled.div``;
+export const Body = styled.div`
+ margin: 24px;
+`;
export const Footer = styled.div``;
diff --git a/config-ui/src/components/page-header/styled.ts
b/config-ui/src/components/page-header/styled.ts
index 375bffec8..05ec967e5 100644
--- a/config-ui/src/components/page-header/styled.ts
+++ b/config-ui/src/components/page-header/styled.ts
@@ -24,7 +24,7 @@ export const Title = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
- margin-bottom: 24px;
+ margin-bottom: 36px;
`;
export const Content = styled.div``;
@@ -35,7 +35,7 @@ export const Breadcrumbs = styled.ul`
`;
export const Breadcrumb = styled.li`
- font-size: 20px;
+ font-size: 24px;
font-weight: 600;
a {
diff --git a/config-ui/src/components/table/styled.ts
b/config-ui/src/components/table/styled.ts
index 59a76225c..c6ee203b6 100644
--- a/config-ui/src/components/table/styled.ts
+++ b/config-ui/src/components/table/styled.ts
@@ -32,13 +32,7 @@ export const THeader = styled.thead`
export const TBody = styled.tbody``;
-export const TR = styled.tr`
- &:last-child {
- td {
- border-bottom: none;
- }
- }
-`;
+export const TR = styled.tr``;
export const TH = styled.th`
padding: 12px 16px;
diff --git a/config-ui/src/layouts/base/use-menu.ts
b/config-ui/src/layouts/base/use-menu.ts
index 864fae96d..9e87a25c4 100644
--- a/config-ui/src/layouts/base/use-menu.ts
+++ b/config-ui/src/layouts/base/use-menu.ts
@@ -19,8 +19,6 @@
import { useMemo } from 'react';
import { IconName } from '@blueprintjs/core';
-import { PluginConfig, PluginType } from '@/plugins';
-
export type MenuItemType = {
key: string;
title: string;
@@ -42,13 +40,6 @@ export const useMenu = () => {
title: 'Connections',
icon: 'data-connection',
path: '/connections',
- children: PluginConfig.filter((p) => p.type ===
PluginType.Connection).map((it) => ({
- key: it.plugin,
- title: it.name,
- iconUrl: it.icon,
- path: `/connections/${it.plugin}`,
- isBeta: it.isBeta,
- })),
},
{
key: 'project',
diff --git a/config-ui/src/pages/blueprint/create/components/step-1.tsx
b/config-ui/src/pages/blueprint/create/components/step-1.tsx
index 725ac5918..1e7353296 100644
--- a/config-ui/src/pages/blueprint/create/components/step-1.tsx
+++ b/config-ui/src/pages/blueprint/create/components/step-1.tsx
@@ -92,7 +92,7 @@ export const Step1 = ({ from }: Props) => {
onChangeItems={(selectedItems) => {
const lastItem = selectedItems[selectedItems.length - 1];
if (lastItem) {
- onTest(lastItem);
+ onTest(lastItem.unique);
}
onChangeConnections(
selectedItems.map((sc) => {
@@ -124,7 +124,7 @@ export const Step1 = ({ from }: Props) => {
size={14}
icon="repeat"
style={{ marginRight: 4, cursor: 'pointer' }}
- onClick={() => onTest(cs)}
+ onClick={() => onTest(cs.unique)}
/>
)}
{cs.status}
diff --git a/config-ui/src/pages/connection/list/api.ts
b/config-ui/src/pages/connection/detail/api.ts
similarity index 100%
rename from config-ui/src/pages/connection/list/api.ts
rename to config-ui/src/pages/connection/detail/api.ts
diff --git a/config-ui/src/pages/connection/detail/index.tsx
b/config-ui/src/pages/connection/detail/index.tsx
new file mode 100644
index 000000000..4ce9a8dc3
--- /dev/null
+++ b/config-ui/src/pages/connection/detail/index.tsx
@@ -0,0 +1,152 @@
+/*
+ * 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 { useState } from 'react';
+import { useParams, useHistory } from 'react-router-dom';
+import { Button, Icon, Intent } from '@blueprintjs/core';
+
+import { PageHeader, Dialog, IconButton } from '@/components';
+import { transformEntities } from '@/config';
+import { ConnectionForm } from '@/plugins';
+import type { ConnectionItemType } from '@/store';
+import { ConnectionContextProvider, useConnection, ConnectionStatus } from
'@/store';
+import { operator } from '@/utils';
+
+import * as API from './api';
+import * as S from './styled';
+
+interface Props {
+ plugin: string;
+ id: ID;
+}
+
+const ConnectionDetail = ({ plugin, id }: Props) => {
+ const [type, setType] = useState<'deleteConnection' | 'updateConnection'>();
+ const [operating, setOperating] = useState(false);
+
+ const history = useHistory();
+ const { connections, onRefresh, onTest } = useConnection();
+ const { unique, status, name, icon, entities } = connections.find(
+ (cs) => cs.unique === `${plugin}-${id}`,
+ ) as ConnectionItemType;
+
+ const handleShowDeleteDialog = () => {
+ setType('deleteConnection');
+ };
+
+ const handleDelete = async () => {
+ const [success] = await operator(() => API.deleteConnection(plugin, id), {
+ setOperating,
+ formatMessage: () => 'Delete Connection Successful.',
+ });
+
+ if (success) {
+ history.push('/connections');
+ }
+ };
+
+ const handleShowUpdateDialog = () => {
+ setType('updateConnection');
+ };
+
+ const handleUpdate = () => {
+ setType(undefined);
+ onRefresh(plugin);
+ };
+
+ const handleHideDialog = () => {
+ setType(undefined);
+ };
+
+ return (
+ <PageHeader
+ breadcrumbs={[
+ { name: 'Connections', path: '/connections' },
+ { name, path: '' },
+ ]}
+ extra={<Button intent={Intent.DANGER} icon="trash" text="Delete
Connection" onClick={handleShowDeleteDialog} />}
+ >
+ <S.Wrapper>
+ <div className="top">
+ <div className="entities">
+ <h3>Data Entities</h3>
+ <span>
+ {transformEntities(entities)
+ .map((it) => it.label)
+ .join(',')}
+ </span>
+ </div>
+ <div className="authentication">
+ <h3>
+ <span>Authentication</span>
+ <IconButton icon="annotation" tooltip="Edit Connection"
onClick={handleShowUpdateDialog} />
+ </h3>
+ <span>Status: </span>
+ <span>
+ Status: <ConnectionStatus status={status} unique={unique}
onTest={onTest} />
+ </span>
+ </div>
+ </div>
+ </S.Wrapper>
+ {type === 'deleteConnection' && (
+ <Dialog
+ isOpen
+ title="Would you like to delete this Data Connection?"
+ okText="Confirm"
+ okLoading={operating}
+ onCancel={handleHideDialog}
+ onOk={handleDelete}
+ >
+ <S.DialogBody>
+ <Icon icon="warning-sign" />
+ <span>
+ This operation cannot be undone. Deleting a Data Connection will
delete all data that have been collected
+ in this Connection.
+ </span>
+ </S.DialogBody>
+ </Dialog>
+ )}
+ {type === 'updateConnection' && (
+ <Dialog
+ style={{ width: 820 }}
+ footer={null}
+ isOpen
+ title={
+ <S.DialogTitle>
+ <img src={icon} alt="" />
+ <span>Authentication</span>
+ </S.DialogTitle>
+ }
+ onCancel={handleHideDialog}
+ >
+ <ConnectionForm plugin={plugin} connectionId={id}
onSuccess={handleUpdate} />
+ </Dialog>
+ )}
+ </PageHeader>
+ );
+};
+
+export const ConnectionDetailPage = () => {
+ const { plugin, id } = useParams<{ plugin: string; id: string }>();
+
+ return (
+ <ConnectionContextProvider plugin={plugin}>
+ <ConnectionDetail plugin={plugin} id={id} />
+ </ConnectionContextProvider>
+ );
+};
diff --git a/config-ui/src/pages/connection/list/styled.ts
b/config-ui/src/pages/connection/detail/styled.ts
similarity index 70%
rename from config-ui/src/pages/connection/list/styled.ts
rename to config-ui/src/pages/connection/detail/styled.ts
index 9cf17b4e3..e8b6e0212 100644
--- a/config-ui/src/pages/connection/list/styled.ts
+++ b/config-ui/src/pages/connection/detail/styled.ts
@@ -19,29 +19,38 @@
import styled from 'styled-components';
export const Wrapper = styled.div`
- .action {
- margin-bottom: 16px;
+ .top {
+ display: flex;
+ justify-content: space-between;
+
+ h3 {
+ margin-bottom: 16px;
+ }
+ }
- .bp4-button + .bp4-button {
- margin-left: 8px;
+ .authentication {
+ h3 {
+ span
}
}
`;
-export const DeleteConfirm = styled.div`
- padding: 16px 24px;
+export const DialogTitle = styled.div`
+ display: flex;
+ align-items: center;
- h3 {
- margin: 0;
- padding: 0;
+ img {
+ margin-right: 8px;
+ width: 24px;
}
+`;
- p {
- margin: 8px 0;
- }
+export const DialogBody = styled.div`
+ display: flex;
+ align-items: center;
- .bp4-button-group {
- display: flex;
- justify-content: flex-end;
+ .bp4-icon {
+ margin-right: 8px;
+ color: #f4be55;
}
`;
diff --git a/config-ui/src/pages/connection/form/index.tsx
b/config-ui/src/pages/connection/form/index.tsx
deleted file mode 100644
index 518ea6a45..000000000
--- a/config-ui/src/pages/connection/form/index.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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 React, { useMemo } from 'react';
-import { useParams } from 'react-router-dom';
-
-import { PageHeader } from '@/components';
-import { ConnectionForm, getPluginConfig } from '@/plugins';
-
-export const ConnectionFormPage = () => {
- const { plugin, cid } = useParams<{ plugin: string; cid?: string }>();
-
- const { name } = useMemo(() => getPluginConfig(plugin), [plugin]);
-
- return (
- <PageHeader
- breadcrumbs={[
- { name: 'Connections', path: '/connections' },
- { name, path: `/connections/${plugin}` },
- {
- name: cid ? cid : 'Create a New Connection',
- path: `/connections/${plugin}/${cid ? cid : 'create'}`,
- },
- ]}
- >
- <ConnectionForm plugin={plugin} connectionId={cid} />
- </PageHeader>
- );
-};
diff --git a/config-ui/src/pages/connection/home/count.tsx
b/config-ui/src/pages/connection/home/count.tsx
deleted file mode 100644
index 78fce8afe..000000000
--- a/config-ui/src/pages/connection/home/count.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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 * as S from './styled';
-
-interface Props {
- count: number;
-}
-
-export const Count = ({ count }: Props) => {
- return <S.Count>{count ? `${count} connections` : 'No connection'}</S.Count>;
-};
diff --git a/config-ui/src/pages/connection/home/index.tsx
b/config-ui/src/pages/connection/home/index.tsx
index af62514d9..4367d0143 100644
--- a/config-ui/src/pages/connection/home/index.tsx
+++ b/config-ui/src/pages/connection/home/index.tsx
@@ -16,77 +16,175 @@
*
*/
-import { useMemo } from 'react';
-import { useHistory } from 'react-router-dom';
-import { Tag, Intent } from '@blueprintjs/core';
+import { useState, useMemo } from 'react';
+import { Link } from 'react-router-dom';
+import { Tag, Intent, Button } from '@blueprintjs/core';
+import { Dialog, Table } from '@/components';
import type { PluginConfigType } from '@/plugins';
-import { PluginConfig, PluginType } from '@/plugins';
-import { ConnectionContextProvider, ConnectionContextConsumer } from '@/store';
+import { PluginConfig, PluginType, ConnectionForm } from '@/plugins';
+import { ConnectionContextProvider, useConnection, ConnectionStatus } from
'@/store';
-import { Count } from './count';
import * as S from './styled';
-export const ConnectionHomePage = () => {
- const history = useHistory();
+export const ConnectionHome = () => {
+ const [type, setType] = useState<'list' | 'form'>();
+ const [pluginConfig, setPluginConfig] = useState<PluginConfigType>();
+
+ const { connections, onRefresh, onTest } = useConnection();
const [plugins, webhook] = useMemo(
() => [
- PluginConfig.filter((p) => p.type === PluginType.Connection && p.plugin
!== 'webhook'),
- PluginConfig.find((p) => p.plugin === 'webhook') as PluginConfigType,
+ PluginConfig.filter((p) => p.type === PluginType.Connection && p.plugin
!== 'webhook').map((p) => ({
+ ...p,
+ count: connections.filter((cs) => cs.plugin === p.plugin).length,
+ })),
+ {
+ ...(PluginConfig.find((p) => p.plugin === 'webhook') as
PluginConfigType),
+ count: connections.filter((cs) => cs.plugin === 'webhook').length,
+ },
],
[],
);
+ const handleShowListDialog = (config: PluginConfigType) => {
+ setType('list');
+ setPluginConfig(config);
+ };
+
+ const handleShowFormDialog = () => {
+ setType('form');
+ };
+
+ const handleHideDialog = () => {
+ setType(undefined);
+ setPluginConfig(undefined);
+ };
+
+ const handleCreateSuccess = async (unqie: string, plugin: string) => {
+ onRefresh(plugin);
+ setType('list');
+ };
+
+ return (
+ <S.Wrapper>
+ <div className="block">
+ <h1>Connections</h1>
+ <h5>
+ Create and manage data connections from the following data sources
or Webhooks to be used in syncing data in
+ your Projects.
+ </h5>
+ </div>
+ <div className="block">
+ <h2>Data Connections</h2>
+ <h5>
+ You can create and manage data connections for the following data
sources and use them in your Projects.
+ </h5>
+ <ul>
+ {plugins.map((p) => (
+ <li key={p.plugin} onClick={() => handleShowListDialog(p)}>
+ <img src={p.icon} alt="" />
+ <span className="name">{p.name}</span>
+ <S.Count>{p.count ? `${p.count} connections` : 'No
connection'}</S.Count>
+ {p.isBeta && (
+ <Tag intent={Intent.WARNING} round>
+ beta
+ </Tag>
+ )}
+ </li>
+ ))}
+ </ul>
+ </div>
+ <div className="block">
+ <h2>Webhooks</h2>
+ <h5>
+ You can use webhooks to import deployments and incidents from the
unsupported data integrations to calculate
+ DORA metrics, etc.
+ </h5>
+ <ul>
+ <li onClick={() => handleShowListDialog(webhook)}>
+ <img src={webhook.icon} alt="" />
+ <span className="name">{webhook.name}</span>
+ <S.Count>{webhook.count ? `${webhook.count} connections` : 'No
connection'}</S.Count>
+ </li>
+ </ul>
+ </div>
+ {type === 'list' && pluginConfig && (
+ <Dialog
+ style={{ width: 820 }}
+ isOpen
+ title={
+ <S.DialogTitle>
+ <img src={pluginConfig.icon} alt="" />
+ <span>Manage Connections: {pluginConfig.name}</span>
+ </S.DialogTitle>
+ }
+ footer={null}
+ onCancel={handleHideDialog}
+ >
+ <Table
+ noShadow
+ columns={[
+ {
+ title: 'Connection Name',
+ dataIndex: 'name',
+ key: 'name',
+ },
+ {
+ title: 'Status',
+ dataIndex: ['status', 'unique'],
+ key: 'status',
+ render: ({ status, unique }) => <ConnectionStatus
status={status} unique={unique} onTest={onTest} />,
+ },
+ {
+ title: '',
+ dataIndex: ['plugin', 'id'],
+ key: 'link',
+ width: 100,
+ render: ({ plugin, id }) => <Link
to={`/connections/${plugin}/${id}`}>Details</Link>,
+ },
+ ]}
+ dataSource={connections.filter((cs) => cs.plugin ===
pluginConfig.plugin)}
+ noData={{
+ text: 'There is no data connection yet. Please add a new
connection.',
+ }}
+ />
+ <Button
+ style={{ marginTop: 16 }}
+ intent={Intent.PRIMARY}
+ icon="add"
+ text="Create a New Connection"
+ onClick={handleShowFormDialog}
+ />
+ </Dialog>
+ )}
+ {type === 'form' && pluginConfig && (
+ <Dialog
+ style={{ width: 820 }}
+ isOpen
+ title={
+ <S.DialogTitle>
+ <img src={pluginConfig.icon} alt="" />
+ <span>Manage Connections: {pluginConfig.name}</span>
+ </S.DialogTitle>
+ }
+ footer={null}
+ onCancel={handleHideDialog}
+ >
+ <ConnectionForm
+ plugin={pluginConfig.plugin}
+ onSuccess={(unique) => handleCreateSuccess(unique,
pluginConfig.plugin)}
+ />
+ </Dialog>
+ )}
+ </S.Wrapper>
+ );
+};
+
+export const ConnectionHomePage = () => {
return (
<ConnectionContextProvider>
- <ConnectionContextConsumer>
- {({ connections }) => (
- <S.Wrapper>
- <div className="block">
- <h1>Connections</h1>
- <h5>
- Create and manage data connections from the following data
sources or Webhooks to be used in syncing
- data in your Projects.
- </h5>
- </div>
- <div className="block">
- <h2>Data Connections</h2>
- <h5>
- You can create and manage data connections for the following
data sources and use them in your Projects.
- </h5>
- <ul>
- {plugins.map((p) => (
- <li key={p.plugin} onClick={() =>
history.push(`/connections/${p.plugin}`)}>
- <img src={p.icon} alt="" />
- <span className="name">{p.name}</span>
- <Count count={connections.filter((cs) => cs.plugin ===
p.plugin).length} />
- {p.isBeta && (
- <Tag intent={Intent.WARNING} round>
- beta
- </Tag>
- )}
- </li>
- ))}
- </ul>
- </div>
- <div className="block">
- <h2>Webhooks</h2>
- <h5>
- You can use webhooks to import deployments and incidents from
the unsupported data integrations to
- calculate DORA metrics, etc.
- </h5>
- <ul>
- <li onClick={() =>
history.push(`/connections/${webhook.plugin}`)}>
- <img src={webhook.icon} alt="" />
- <span className="name">{webhook.name}</span>
- <Count count={connections.filter((cs) => cs.plugin ===
'webhook').length} />
- </li>
- </ul>
- </div>
- </S.Wrapper>
- )}
- </ConnectionContextConsumer>
+ <ConnectionHome />
</ConnectionContextProvider>
);
};
diff --git a/config-ui/src/pages/connection/home/styled.ts
b/config-ui/src/pages/connection/home/styled.ts
index 05cba9ee9..601ae9f42 100644
--- a/config-ui/src/pages/connection/home/styled.ts
+++ b/config-ui/src/pages/connection/home/styled.ts
@@ -85,3 +85,13 @@ export const Wrapper = styled.div`
export const Count = styled.span`
color: #70727f;
`;
+
+export const DialogTitle = styled.div`
+ display: flex;
+ align-items: center;
+
+ img {
+ margin-right: 8px;
+ width: 24px;
+ }
+`;
diff --git a/config-ui/src/pages/connection/index.ts
b/config-ui/src/pages/connection/index.ts
index cdf832ea7..d5131a681 100644
--- a/config-ui/src/pages/connection/index.ts
+++ b/config-ui/src/pages/connection/index.ts
@@ -17,5 +17,4 @@
*/
export * from './home';
-export * from './list';
-export * from './form';
+export * from './detail';
diff --git a/config-ui/src/pages/connection/list/connection.tsx
b/config-ui/src/pages/connection/list/connection.tsx
deleted file mode 100644
index 5ef877039..000000000
--- a/config-ui/src/pages/connection/list/connection.tsx
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * 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 React, { useState, useEffect, useMemo } from 'react';
-import { useHistory } from 'react-router-dom';
-import { ButtonGroup, Button, Intent, Position } from '@blueprintjs/core';
-import { Popover2 } from '@blueprintjs/popover2';
-
-import { Table, ColumnType, IconButton } from '@/components';
-import type { ConnectionItemType } from '@/store';
-import { useConnection, ConnectionStatus } from '@/store';
-import { operator } from '@/utils';
-
-import * as API from './api';
-import * as S from './styled';
-
-interface Props {
- plugin: string;
-}
-
-export const Connection = ({ plugin }: Props) => {
- const [operating, setOperating] = useState(false);
-
- const history = useHistory();
-
- const { connections, onTest, onRefresh } = useConnection();
-
- useEffect(() => {
- connections.map((cs) => onTest(cs));
- }, []);
-
- const handleRefresh = () => onRefresh();
-
- const handleCreate = () => history.push(`/connections/${plugin}/create`);
-
- const handleUpdate = (id: ID) =>
history.push(`/connections/${plugin}/${id}`);
-
- const handleDelete = async (id: ID) => {
- const [success] = await operator(() => API.deleteConnection(plugin, id), {
- setOperating,
- });
-
- if (success) {
- onRefresh();
- }
- };
-
- const columns = useMemo(
- () =>
- [
- {
- title: 'ID',
- dataIndex: 'id',
- key: 'id',
- width: 100,
- },
- {
- title: 'Connection Name',
- dataIndex: 'name',
- key: 'name',
- },
- {
- title: 'Endpoint',
- dataIndex: 'endpoint',
- key: 'endpoint',
- ellipsis: true,
- },
- {
- title: 'Status',
- dataIndex: 'status',
- key: 'status',
- align: 'center',
- render: (_, row) => <ConnectionStatus connection={row}
onTest={onTest} />,
- },
- {
- title: '',
- dataIndex: 'id',
- key: 'action',
- width: 100,
- align: 'center',
- render: (id) => (
- <ButtonGroup>
- <IconButton icon="edit" tooltip="Edit" onClick={() =>
handleUpdate(id)} />
- <Popover2
- position={Position.TOP}
- content={
- <S.DeleteConfirm>
- <h3>Confirm deletion</h3>
- <p>Are you sure you want to delete this item?</p>
- <ButtonGroup>
- <Button
- loading={operating}
- intent={Intent.DANGER}
- text="Delete"
- onClick={() => handleDelete(id)}
- />
- </ButtonGroup>
- </S.DeleteConfirm>
- }
- >
- <IconButton icon="delete" tooltip="Delete" />
- </Popover2>
- </ButtonGroup>
- ),
- },
- ] as ColumnType<ConnectionItemType>,
- [],
- );
-
- return (
- <S.Wrapper>
- <ButtonGroup className="action">
- <Button intent={Intent.PRIMARY} icon="plus" text="New Connection"
onClick={handleCreate} />
- <Button icon="refresh" text="Refresh Connections"
onClick={handleRefresh} />
- </ButtonGroup>
- <Table
- columns={columns}
- dataSource={connections}
- noData={{
- text: 'There is no data connection yet. Please add a new
connection.',
- btnText: 'New Connection',
- onCreate: handleCreate,
- }}
- />
- </S.Wrapper>
- );
-};
diff --git a/config-ui/src/pages/connection/list/index.tsx
b/config-ui/src/pages/connection/list/index.tsx
deleted file mode 100644
index e44bd603a..000000000
--- a/config-ui/src/pages/connection/list/index.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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 React, { useMemo } from 'react';
-import { useParams } from 'react-router-dom';
-
-import { PageHeader } from '@/components';
-import { getPluginConfig } from '@/plugins';
-import { WebHookConnection } from '@/plugins/register/webook';
-import { ConnectionContextProvider } from '@/store';
-
-import { Connection } from './connection';
-
-export const ConnectionListPage = () => {
- const { plugin } = useParams<{ plugin: string }>();
-
- const config = useMemo(() => getPluginConfig(plugin), [plugin]);
-
- return (
- <ConnectionContextProvider plugin={plugin}>
- <PageHeader
- breadcrumbs={[
- { name: 'Connections', path: '/connections' },
- { name: config.name, path: `/connections/${plugin}` },
- ]}
- >
- {plugin === 'webhook' ? <WebHookConnection /> : <Connection
plugin={plugin} />}
- </PageHeader>
- </ConnectionContextProvider>
- );
-};
diff --git a/config-ui/src/plugins/components/connection-form/index.tsx
b/config-ui/src/plugins/components/connection-form/index.tsx
index f9ea7c88c..b2ec47e62 100644
--- a/config-ui/src/plugins/components/connection-form/index.tsx
+++ b/config-ui/src/plugins/components/connection-form/index.tsx
@@ -16,32 +16,39 @@
*
*/
-import React, { useMemo, useState } from 'react';
-import { ButtonGroup } from '@blueprintjs/core';
+import { useMemo, useState } from 'react';
+import { ButtonGroup, Button, Intent } from '@blueprintjs/core';
+import { pick } from 'lodash';
import { ExternalLink, PageLoading } from '@/components';
import { useRefreshData } from '@/hooks';
import { getPluginConfig } from '@/plugins';
+import { operator } from '@/utils';
import { Form } from './fields';
-import { Save, Test } from './operate';
import * as API from './api';
import * as S from './styled';
interface Props {
plugin: string;
connectionId?: ID;
+ onSuccess?: (unique: string) => void;
}
-export const ConnectionForm = ({ plugin, connectionId }: Props) => {
+export const ConnectionForm = ({ plugin, connectionId, onSuccess }: Props) => {
const [values, setValues] = useState<Record<string, any>>({});
const [errors, setErrors] = useState<Record<string, any>>({});
+ const [operating, setOperating] = useState(false);
const {
name,
connection: { docLink, fields, initialValues },
} = useMemo(() => getPluginConfig(plugin), [plugin]);
+ const disabled = useMemo(() => {
+ return Object.values(errors).some((value) => value);
+ }, [errors]);
+
const { ready, data } = useRefreshData(async () => {
if (!connectionId) {
return {};
@@ -50,6 +57,45 @@ export const ConnectionForm = ({ plugin, connectionId }:
Props) => {
return API.getConnection(plugin, connectionId);
}, [plugin, connectionId]);
+ const handleTest = async () => {
+ await operator(
+ () =>
+ API.testConnection(
+ plugin,
+ pick(values, [
+ 'endpoint',
+ 'token',
+ 'username',
+ 'password',
+ 'proxy',
+ 'authMethod',
+ 'appId',
+ 'secretKey',
+ 'tenantId',
+ 'tenantType',
+ ]),
+ ),
+ {
+ setOperating,
+ formatMessage: () => 'Test Connection Successfully.',
+ },
+ );
+ };
+
+ const handleSave = async () => {
+ const [success, res] = await operator(
+ () => (!connectionId ? API.createConnection(plugin, values) :
API.updateConnection(plugin, connectionId, values)),
+ {
+ setOperating,
+ formatMessage: () => (!connectionId ? 'Create a New Connection
Successful.' : 'Update Connection Successful.'),
+ },
+ );
+
+ if (success) {
+ onSuccess?.(`${plugin}-${res.id}`);
+ }
+ };
+
if (connectionId && !ready) {
return <PageLoading />;
}
@@ -71,8 +117,15 @@ export const ConnectionForm = ({ plugin, connectionId }:
Props) => {
setErrors={setErrors}
/>
<ButtonGroup className="btns">
- <Test plugin={plugin} values={values} errors={errors} />
- <Save plugin={plugin} connectionId={connectionId} values={values}
errors={errors} />
+ <Button loading={operating} disabled={disabled} outlined text="Test
Connection" onClick={handleTest} />
+ <Button
+ loading={operating}
+ disabled={disabled}
+ intent={Intent.PRIMARY}
+ outlined
+ text="Save Connection"
+ onClick={handleSave}
+ />
</ButtonGroup>
</S.Form>
</S.Wrapper>
diff --git a/config-ui/src/plugins/components/connection-form/operate/index.ts
b/config-ui/src/plugins/components/connection-form/operate/index.ts
deleted file mode 100644
index 2dda77f3f..000000000
--- a/config-ui/src/plugins/components/connection-form/operate/index.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * 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.
- *
- */
-
-export * from './test';
-export * from './save';
diff --git a/config-ui/src/plugins/components/connection-form/operate/save.tsx
b/config-ui/src/plugins/components/connection-form/operate/save.tsx
deleted file mode 100644
index a4143b806..000000000
--- a/config-ui/src/plugins/components/connection-form/operate/save.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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 { useState, useMemo } from 'react';
-import { useHistory } from 'react-router-dom';
-import { Button, Intent } from '@blueprintjs/core';
-
-import { operator } from '@/utils';
-
-import * as API from '../api';
-
-interface Props {
- plugin: string;
- connectionId?: ID;
- values: any;
- errors: any;
-}
-
-export const Save = ({ plugin, connectionId, values, errors }: Props) => {
- const [saving, setSaving] = useState(false);
- const history = useHistory();
-
- const handleSubmit = async () => {
- const [success] = await operator(
- () => (!connectionId ? API.createConnection(plugin, values) :
API.updateConnection(plugin, connectionId, values)),
- {
- setOperating: setSaving,
- },
- );
-
- if (success) {
- history.push(`/connections/${plugin}`);
- }
- };
-
- const disabled = useMemo(() => {
- return Object.values(errors).some((value) => value);
- }, [errors]);
-
- return (
- <Button
- loading={saving}
- disabled={disabled}
- intent={Intent.PRIMARY}
- outlined
- text="Save Connection"
- onClick={handleSubmit}
- />
- );
-};
diff --git a/config-ui/src/plugins/components/connection-form/operate/test.tsx
b/config-ui/src/plugins/components/connection-form/operate/test.tsx
deleted file mode 100644
index f2222a691..000000000
--- a/config-ui/src/plugins/components/connection-form/operate/test.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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 { useState, useMemo } from 'react';
-import { Button } from '@blueprintjs/core';
-import { pick } from 'lodash';
-
-import { operator } from '@/utils';
-
-import * as API from '../api';
-
-interface Props {
- plugin: string;
- values: any;
- errors: any;
-}
-
-export const Test = ({ plugin, values, errors }: Props) => {
- const [testing, setTesting] = useState(false);
-
- const disabled = useMemo(() => {
- return Object.values(errors).some((value) => value);
- }, [errors]);
-
- const handleSubmit = async () => {
- await operator(
- () =>
- API.testConnection(
- plugin,
- pick(values, [
- 'endpoint',
- 'token',
- 'username',
- 'password',
- 'proxy',
- 'authMethod',
- 'appId',
- 'secretKey',
- 'tenantId',
- 'tenantType',
- ]),
- ),
- {
- setOperating: setTesting,
- },
- );
- };
-
- return <Button loading={testing} disabled={disabled} outlined text="Test
Connection" onClick={handleSubmit} />;
-};
diff --git a/config-ui/src/plugins/components/connection-form/styled.ts
b/config-ui/src/plugins/components/connection-form/styled.ts
index 09c870e4d..8012bf159 100644
--- a/config-ui/src/plugins/components/connection-form/styled.ts
+++ b/config-ui/src/plugins/components/connection-form/styled.ts
@@ -21,7 +21,7 @@ import styled from 'styled-components';
export const Wrapper = styled.div``;
export const Tips = styled.div`
- margin-bottom: 36px;
+ margin-bottom: 24px;
padding: 24px;
color: #3c5088;
background: #f0f4fe;
@@ -30,11 +30,6 @@ export const Tips = styled.div`
`;
export const Form = styled.div`
- padding: 24px;
- background: #ffffff;
- box-shadow: 0px 2.4px 4.8px -0.8px rgba(0, 0, 0, 0.1), 0px 1.6px 8px rgba(0,
0, 0, 0.07);
- border-radius: 8px;
-
.bp4-form-group label.bp4-label {
margin: 0 0 8px 0;
}
diff --git a/config-ui/src/plugins/components/transformation-form/misc.ts
b/config-ui/src/plugins/components/transformation-form/misc.ts
index b330ba7cf..8cb8bc2c7 100644
--- a/config-ui/src/plugins/components/transformation-form/misc.ts
+++ b/config-ui/src/plugins/components/transformation-form/misc.ts
@@ -23,7 +23,7 @@ export const TIPS_MAP: Record<string, { name: string; link:
string }> = {
},
gitlab: {
name: 'GitLab',
- link:
'https://devlake.apache.org/docs/Configuration/GitLab#step-3---adding-transformation-rules-optional'
+ link:
'https://devlake.apache.org/docs/Configuration/GitLab#step-3---adding-transformation-rules-optional',
},
jira: {
name: 'Jira',
@@ -31,18 +31,18 @@ export const TIPS_MAP: Record<string, { name: string; link:
string }> = {
},
jenkins: {
name: 'Jenkins',
- link:
'https://devlake.apache.org/docs/Configuration/Jenkins#step-3---adding-transformation-rules-optional'
+ link:
'https://devlake.apache.org/docs/Configuration/Jenkins#step-3---adding-transformation-rules-optional',
},
bitbucket: {
name: 'BitBucket',
- link:
'https://devlake.apache.org/docs/Configuration/BitBucket#step-3---adding-transformation-rules-optional'
+ link:
'https://devlake.apache.org/docs/Configuration/BitBucket#step-3---adding-transformation-rules-optional',
},
azuredevops: {
name: 'Azure DevOps',
- link:
'https://devlake.apache.org/docs/Configuration/Jenkins#step-3---adding-transformation-rules-optional'
+ link:
'https://devlake.apache.org/docs/Configuration/Jenkins#step-3---adding-transformation-rules-optional',
},
tapd: {
- name:'TAPD',
- link:
'https://devlake.apache.org/docs/Configuration/Tapd#step-3---adding-transformation-rules-optional'
+ name: 'TAPD',
+ link:
'https://devlake.apache.org/docs/Configuration/Tapd#step-3---adding-transformation-rules-optional',
},
};
diff --git a/config-ui/src/plugins/config.ts b/config-ui/src/plugins/config.ts
index 542e6c7a8..29fefa6c4 100644
--- a/config-ui/src/plugins/config.ts
+++ b/config-ui/src/plugins/config.ts
@@ -17,6 +17,7 @@
*/
import type { PluginConfigType } from './types';
+import { BasePipelineConfig } from './register/base';
import { AEConfig } from './register/ae';
import { AzureConfig } from './register/azure';
import { BitBucketConfig } from './register/bitbucket';
@@ -38,9 +39,8 @@ import { SonarQubeConfig } from './register/sonarqube';
import { StarRocksConfig } from './register/starrocks';
import { TAPDConfig } from './register/tapd';
import { WebhookConfig } from './register/webook';
-import { ZenTaoConfig } from './register/zentao';
import { TeambitionConfig } from './register/teambition';
-import { BasePipelineConfig } from '@/plugins/register/base';
+import { ZenTaoConfig } from './register/zentao';
export const PluginConfig: PluginConfigType[] = [
AEConfig,
diff --git a/config-ui/src/plugins/register/ae/config.ts
b/config-ui/src/plugins/register/ae/config.ts
index f0d873cb3..efcf1b87b 100644
--- a/config-ui/src/plugins/register/ae/config.ts
+++ b/config-ui/src/plugins/register/ae/config.ts
@@ -16,7 +16,7 @@
*
*/
-import type { PluginConfigType } from '@/plugins';
+import type { PluginConfigType } from '../../types';
import { BasePipelineConfig } from '../base';
diff --git a/config-ui/src/plugins/register/azure/config.tsx
b/config-ui/src/plugins/register/azure/config.tsx
index eea381cf2..0686b3a9c 100644
--- a/config-ui/src/plugins/register/azure/config.tsx
+++ b/config-ui/src/plugins/register/azure/config.tsx
@@ -16,11 +16,10 @@
*
*/
-import React from 'react';
-
import { ExternalLink } from '@/components';
-import type { PluginConfigType } from '@/plugins';
-import { PluginType } from '@/plugins';
+
+import type { PluginConfigType } from '../../types';
+import { PluginType } from '../../types';
import Icon from './assets/icon.svg';
import { BaseURL } from './connection-fields';
diff --git a/config-ui/src/plugins/register/bitbucket/config.tsx
b/config-ui/src/plugins/register/bitbucket/config.tsx
index 8d7edb9ea..7518bd041 100644
--- a/config-ui/src/plugins/register/bitbucket/config.tsx
+++ b/config-ui/src/plugins/register/bitbucket/config.tsx
@@ -16,8 +16,8 @@
*
*/
-import type { PluginConfigType } from '@/plugins';
-import { PluginType } from '@/plugins';
+import type { PluginConfigType } from '../../types';
+import { PluginType } from '../../types';
import Icon from './assets/icon.svg';
diff --git a/config-ui/src/plugins/register/customize/config.ts
b/config-ui/src/plugins/register/customize/config.ts
index e5bebbb5d..6def51839 100644
--- a/config-ui/src/plugins/register/customize/config.ts
+++ b/config-ui/src/plugins/register/customize/config.ts
@@ -16,7 +16,7 @@
*
*/
-import type { PluginConfigType } from '@/plugins';
+import type { PluginConfigType } from '../../types';
import { BasePipelineConfig } from '../base';
diff --git a/config-ui/src/plugins/register/dbt/config.ts
b/config-ui/src/plugins/register/dbt/config.ts
index 8b19b8ea9..90b1d3e63 100644
--- a/config-ui/src/plugins/register/dbt/config.ts
+++ b/config-ui/src/plugins/register/dbt/config.ts
@@ -16,7 +16,7 @@
*
*/
-import type { PluginConfigType } from '@/plugins';
+import type { PluginConfigType } from '../../types';
import { BasePipelineConfig } from '../base';
diff --git a/config-ui/src/plugins/register/dora/config.ts
b/config-ui/src/plugins/register/dora/config.ts
index fe7f06084..5589dc87b 100644
--- a/config-ui/src/plugins/register/dora/config.ts
+++ b/config-ui/src/plugins/register/dora/config.ts
@@ -16,7 +16,7 @@
*
*/
-import type { PluginConfigType } from '@/plugins';
+import type { PluginConfigType } from '../../types';
import { BasePipelineConfig } from '../base';
diff --git a/config-ui/src/plugins/register/feishu/config.ts
b/config-ui/src/plugins/register/feishu/config.ts
index 6be499ffb..6c1a347f7 100644
--- a/config-ui/src/plugins/register/feishu/config.ts
+++ b/config-ui/src/plugins/register/feishu/config.ts
@@ -16,7 +16,7 @@
*
*/
-import type { PluginConfigType } from '@/plugins';
+import type { PluginConfigType } from '../../types';
import { BasePipelineConfig } from '../base';
diff --git a/config-ui/src/plugins/register/gitee/config.ts
b/config-ui/src/plugins/register/gitee/config.ts
index 64d528f49..39267405a 100644
--- a/config-ui/src/plugins/register/gitee/config.ts
+++ b/config-ui/src/plugins/register/gitee/config.ts
@@ -16,7 +16,7 @@
*
*/
-import type { PluginConfigType } from '@/plugins';
+import type { PluginConfigType } from '../../types';
import { BasePipelineConfig } from '../base';
diff --git a/config-ui/src/plugins/register/gitextractor/config.ts
b/config-ui/src/plugins/register/gitextractor/config.ts
index 400941ead..20d746fd9 100644
--- a/config-ui/src/plugins/register/gitextractor/config.ts
+++ b/config-ui/src/plugins/register/gitextractor/config.ts
@@ -16,7 +16,7 @@
*
*/
-import type { PluginConfigType } from '@/plugins';
+import type { PluginConfigType } from '../../types';
import { BasePipelineConfig } from '../base';
diff --git a/config-ui/src/plugins/register/github/config.tsx
b/config-ui/src/plugins/register/github/config.tsx
index a5976b0c4..85698f4bd 100644
--- a/config-ui/src/plugins/register/github/config.tsx
+++ b/config-ui/src/plugins/register/github/config.tsx
@@ -16,10 +16,8 @@
*
*/
-import React from 'react';
-
-import type { PluginConfigType } from '@/plugins';
-import { PluginType } from '@/plugins';
+import type { PluginConfigType } from '../../types';
+import { PluginType } from '../../types';
import Icon from './assets/icon.svg';
import { Token, Graphql } from './connection-fields';
diff --git a/config-ui/src/plugins/register/github_graphql/config.ts
b/config-ui/src/plugins/register/github_graphql/config.ts
index c21edbfba..ca1c8e911 100644
--- a/config-ui/src/plugins/register/github_graphql/config.ts
+++ b/config-ui/src/plugins/register/github_graphql/config.ts
@@ -16,7 +16,7 @@
*
*/
-import type { PluginConfigType } from '@/plugins';
+import type { PluginConfigType } from '../../types';
import { BasePipelineConfig } from '../base';
diff --git a/config-ui/src/plugins/register/gitlab/config.tsx
b/config-ui/src/plugins/register/gitlab/config.tsx
index bf24eefea..19ebbe925 100644
--- a/config-ui/src/plugins/register/gitlab/config.tsx
+++ b/config-ui/src/plugins/register/gitlab/config.tsx
@@ -16,10 +16,8 @@
*
*/
-import React from 'react';
-
-import type { PluginConfigType } from '@/plugins';
-import { PluginType } from '@/plugins';
+import type { PluginConfigType } from '../../types';
+import { PluginType } from '../../types';
import { ExternalLink } from '@/components';
diff --git a/config-ui/src/plugins/register/jenkins/config.ts
b/config-ui/src/plugins/register/jenkins/config.ts
index 354159392..ac63788a0 100644
--- a/config-ui/src/plugins/register/jenkins/config.ts
+++ b/config-ui/src/plugins/register/jenkins/config.ts
@@ -16,8 +16,8 @@
*
*/
-import type { PluginConfigType } from '@/plugins';
-import { PluginType } from '@/plugins';
+import type { PluginConfigType } from '../../types';
+import { PluginType } from '../../types';
import Icon from './assets/icon.svg';
diff --git a/config-ui/src/plugins/register/jira/config.tsx
b/config-ui/src/plugins/register/jira/config.tsx
index 4aebf5158..30915cc3a 100644
--- a/config-ui/src/plugins/register/jira/config.tsx
+++ b/config-ui/src/plugins/register/jira/config.tsx
@@ -16,8 +16,8 @@
*
*/
-import type { PluginConfigType } from '@/plugins';
-import { PluginType } from '@/plugins';
+import type { PluginConfigType } from '../../types';
+import { PluginType } from '../../types';
import Icon from './assets/icon.svg';
import { Auth } from './connection-fields';
diff --git a/config-ui/src/plugins/register/org/config.ts
b/config-ui/src/plugins/register/org/config.ts
index 5b47e7af6..d39717a5b 100644
--- a/config-ui/src/plugins/register/org/config.ts
+++ b/config-ui/src/plugins/register/org/config.ts
@@ -16,7 +16,7 @@
*
*/
-import type { PluginConfigType } from '@/plugins';
+import type { PluginConfigType } from '../../types';
import { BasePipelineConfig } from '../base';
diff --git a/config-ui/src/plugins/register/pagerduty/config.tsx
b/config-ui/src/plugins/register/pagerduty/config.tsx
index b7d2aa1d1..56d1f8a0a 100644
--- a/config-ui/src/plugins/register/pagerduty/config.tsx
+++ b/config-ui/src/plugins/register/pagerduty/config.tsx
@@ -16,8 +16,8 @@
*
*/
-import type { PluginConfigType } from '@/plugins';
-import { PluginType } from '@/plugins';
+import type { PluginConfigType } from '../../types';
+import { PluginType } from '../../types';
import Icon from './assets/icon.svg';
diff --git a/config-ui/src/plugins/register/refdiff/config.ts
b/config-ui/src/plugins/register/refdiff/config.ts
index 916696dfc..ff7864b87 100644
--- a/config-ui/src/plugins/register/refdiff/config.ts
+++ b/config-ui/src/plugins/register/refdiff/config.ts
@@ -16,7 +16,7 @@
*
*/
-import type { PluginConfigType } from '@/plugins';
+import type { PluginConfigType } from '../../types';
import { BasePipelineConfig } from '../base';
diff --git a/config-ui/src/plugins/register/sonarqube/config.ts
b/config-ui/src/plugins/register/sonarqube/config.ts
index 37dfd2c33..f8dce2c3f 100644
--- a/config-ui/src/plugins/register/sonarqube/config.ts
+++ b/config-ui/src/plugins/register/sonarqube/config.ts
@@ -16,8 +16,8 @@
*
*/
-import type { PluginConfigType } from '@/plugins';
-import { PluginType } from '@/plugins';
+import type { PluginConfigType } from '../../types';
+import { PluginType } from '../../types';
import Icon from './assets/icon.svg';
diff --git a/config-ui/src/plugins/register/starrocks/config.ts
b/config-ui/src/plugins/register/starrocks/config.ts
index 1a24fac9f..b9346597b 100644
--- a/config-ui/src/plugins/register/starrocks/config.ts
+++ b/config-ui/src/plugins/register/starrocks/config.ts
@@ -16,7 +16,7 @@
*
*/
-import type { PluginConfigType } from '@/plugins';
+import type { PluginConfigType } from '../../types';
import { BasePipelineConfig } from '../base';
diff --git a/config-ui/src/plugins/register/tapd/config.tsx
b/config-ui/src/plugins/register/tapd/config.tsx
index cd1fb77ad..1c36f8a8e 100644
--- a/config-ui/src/plugins/register/tapd/config.tsx
+++ b/config-ui/src/plugins/register/tapd/config.tsx
@@ -16,11 +16,10 @@
*
*/
-import React from 'react';
-
import { ExternalLink } from '@/components';
-import type { PluginConfigType } from '@/plugins';
-import { PluginType } from '@/plugins';
+
+import type { PluginConfigType } from '../../types';
+import { PluginType } from '../../types';
import Icon from './assets/icon.svg';
diff --git a/config-ui/src/plugins/register/teambition/config.tsx
b/config-ui/src/plugins/register/teambition/config.tsx
index 5b724b4d5..df4e29bec 100644
--- a/config-ui/src/plugins/register/teambition/config.tsx
+++ b/config-ui/src/plugins/register/teambition/config.tsx
@@ -16,10 +16,8 @@
*
*/
-import React from 'react';
-
-import type { PluginConfigType } from '@/plugins';
-import { PluginType } from '@/plugins';
+import type { PluginConfigType } from '../../types';
+import { PluginType } from '../../types';
import Icon from './assets/icon.svg';
import { ConnectionTenantId, ConnectionTenantType } from './connection-fields';
diff --git a/config-ui/src/plugins/register/webook/config.ts
b/config-ui/src/plugins/register/webook/config.ts
index 06510dceb..4aa4a557b 100644
--- a/config-ui/src/plugins/register/webook/config.ts
+++ b/config-ui/src/plugins/register/webook/config.ts
@@ -16,15 +16,17 @@
*
*/
-import { PluginType } from '@/plugins';
+import type { PluginConfigType } from '../../types';
+import { PluginType } from '../../types';
import Icon from './assets/icon.svg';
import { BasePipelineConfig } from '../base';
-export const WebhookConfig = {
+export const WebhookConfig: PluginConfigType = {
...BasePipelineConfig,
plugin: 'webhook',
name: 'Webhook',
type: PluginType.Connection,
icon: Icon,
+ sort: 100,
};
diff --git a/config-ui/src/plugins/register/zentao/config.ts
b/config-ui/src/plugins/register/zentao/config.ts
index 71b67ea09..27f34b16a 100644
--- a/config-ui/src/plugins/register/zentao/config.ts
+++ b/config-ui/src/plugins/register/zentao/config.ts
@@ -16,8 +16,8 @@
*
*/
-import type { PluginConfigType } from '@/plugins';
-import { PluginType } from '@/plugins';
+import type { PluginConfigType } from '../../types';
+import { PluginType } from '../../types';
import Icon from './assets/icon.svg';
diff --git a/config-ui/src/store/connections/api.ts
b/config-ui/src/store/connections/api.ts
index e2ff846ef..d84d04852 100644
--- a/config-ui/src/store/connections/api.ts
+++ b/config-ui/src/store/connections/api.ts
@@ -18,7 +18,18 @@
import { request } from '@/utils';
-export const getConnection = (plugin: string) =>
request(`/plugins/${plugin}/connections`);
+type GetConnectionRes = {
+ id: ID;
+ name: string;
+ endpoint: string;
+ proxy: string;
+ token?: string;
+ username?: string;
+ password?: string;
+ authMethod?: string;
+};
+
+export const getConnection = (plugin: string): Promise<GetConnectionRes[]> =>
request(`/plugins/${plugin}/connections`);
type TestConnectionPayload = {
endpoint: string;
diff --git a/config-ui/src/store/connections/context.tsx
b/config-ui/src/store/connections/context.tsx
index 6054ed823..fcd69c661 100644
--- a/config-ui/src/store/connections/context.tsx
+++ b/config-ui/src/store/connections/context.tsx
@@ -26,8 +26,8 @@ import { useContextValue } from './use-context-value';
const ConnectionContext = React.createContext<{
connections: ConnectionItemType[];
- onRefresh: () => void;
- onTest: (selectedConnection: ConnectionItemType) => void;
+ onRefresh: (plugin?: string) => void;
+ onTest: (unique: string) => void;
}>({
connections: [],
onRefresh: () => {},
diff --git a/config-ui/src/store/connections/status.tsx
b/config-ui/src/store/connections/status.tsx
index 9a551fa75..fe9681d20 100644
--- a/config-ui/src/store/connections/status.tsx
+++ b/config-ui/src/store/connections/status.tsx
@@ -16,13 +16,10 @@
*
*/
-import { Icon, Colors, Position, Intent } from '@blueprintjs/core';
-import { Tooltip2 } from '@blueprintjs/popover2';
import styled from 'styled-components';
-import { Loading } from '@/components';
+import { IconButton } from '@/components';
-import type { ConnectionItemType } from './types';
import { ConnectionStatusEnum } from './types';
const Wrapper = styled.div`
@@ -30,47 +27,39 @@ const Wrapper = styled.div`
align-items: center;
& > span.online {
- color: ${Colors.GREEN3};
+ color: #4db764;
}
& > span.offline {
- color: ${Colors.RED3};
- }
-
- & > span.testing {
- color: #7497f7;
+ color: #e34040;
}
`;
const STATUS_MAP = {
- [`${ConnectionStatusEnum.NULL}`]: 'Init',
+ [`${ConnectionStatusEnum.NULL}`]: 'Test',
[`${ConnectionStatusEnum.TESTING}`]: 'Testing',
- [`${ConnectionStatusEnum.ONLINE}`]: 'Online',
- [`${ConnectionStatusEnum.OFFLINE}`]: 'Offline',
+ [`${ConnectionStatusEnum.ONLINE}`]: 'Connected',
+ [`${ConnectionStatusEnum.OFFLINE}`]: 'Disconnected',
};
interface Props {
- connection: ConnectionItemType;
- onTest: (connection: ConnectionItemType) => void;
+ status: ConnectionStatusEnum;
+ unique: string;
+ onTest: (unique: string) => void;
}
-export const ConnectionStatus = ({ connection, onTest }: Props) => {
- const { status } = connection;
-
+export const ConnectionStatus = ({ status, unique, onTest }: Props) => {
return (
<Wrapper>
- {status === ConnectionStatusEnum.TESTING && <Loading size={14} style={{
marginRight: 4 }} />}
- {status === ConnectionStatusEnum.OFFLINE && (
- <Tooltip2 intent={Intent.PRIMARY} position={Position.TOP}
content="Retry">
- <Icon
- size={14}
- icon="repeat"
- style={{ marginRight: 4, color: Colors.RED3, cursor: 'pointer' }}
- onClick={() => onTest(connection)}
- />
- </Tooltip2>
- )}
<span className={status}>{STATUS_MAP[status]}</span>
+ {status !== ConnectionStatusEnum.ONLINE && (
+ <IconButton
+ loading={status === ConnectionStatusEnum.TESTING}
+ icon="repeat"
+ tooltip="Retry"
+ onClick={() => onTest(unique)}
+ />
+ )}
</Wrapper>
);
};
diff --git a/config-ui/src/store/connections/use-context-value.ts
b/config-ui/src/store/connections/use-context-value.ts
index 369bc8a92..72eeeab18 100644
--- a/config-ui/src/store/connections/use-context-value.ts
+++ b/config-ui/src/store/connections/use-context-value.ts
@@ -16,8 +16,9 @@
*
*/
-import { useState, useEffect, useCallback, useMemo } from 'react';
+import { useState, useEffect, useMemo } from 'react';
+import type { PluginConfigType } from '@/plugins';
import { PluginConfig, PluginType } from '@/plugins';
import type { ConnectionItemType } from './types';
@@ -31,114 +32,122 @@ export interface UseContextValueProps {
filter?: string[];
}
-export const useContextValue = ({ plugin, filterBeta = false, filterPlugin,
filter }: UseContextValueProps) => {
- const [loading, setLoading] = useState(false);
+export const useContextValue = ({ plugin, filterBeta, filterPlugin, filter }:
UseContextValueProps) => {
+ const [loading, setLoading] = useState(true);
const [connections, setConnections] = useState<ConnectionItemType[]>([]);
- const allConnections = useMemo(
+ const plugins = useMemo(
() =>
PluginConfig.filter((p) => p.type === PluginType.Connection)
+ .filter((p) => (plugin ? p.plugin === plugin : true))
.filter((p) => (filterBeta ? !p.isBeta : true))
- .filter((p) => (filterPlugin ? !filterPlugin.includes(p.plugin) :
true))
- .filter((p) => (plugin ? p.plugin === plugin : true)),
+ .filter((p) => (filterPlugin ? !filterPlugin.includes(p.plugin) :
true)),
[plugin],
);
const getConnection = async (plugin: string) => {
try {
- return await API.getConnection(plugin);
+ const res = await API.getConnection(plugin);
+ const { icon, entities } = plugins.find((p) => p.plugin === plugin) as
PluginConfigType;
+
+ return res.map((connection) => ({
+ ...connection,
+ plugin,
+ icon,
+ entities,
+ }));
} catch {
return [];
}
};
- const handleRefresh = useCallback(async () => {
- setLoading(true);
+ const testConnection = async ({
+ plugin,
+ endpoint,
+ proxy,
+ token,
+ username,
+ password,
+ authMethod,
+ }: ConnectionItemType) => {
+ try {
+ const res = await API.testConnection(plugin, {
+ endpoint,
+ proxy,
+ token,
+ username,
+ password,
+ authMethod,
+ });
+ return res.success ? ConnectionStatusEnum.ONLINE :
ConnectionStatusEnum.OFFLINE;
+ } catch {
+ return ConnectionStatusEnum.OFFLINE;
+ }
+ };
- const res = await Promise.all(allConnections.map((cs) =>
getConnection(cs.plugin)));
+ const transformConnection = (connections: Omit<ConnectionItemType, 'unique'
| 'status'>[]) => {
+ return connections.map((it) => ({
+ unique: `${it.plugin}-${it.id}`,
+ status: ConnectionStatusEnum.NULL,
+ plugin: it.plugin,
+ id: it.id,
+ name: it.name,
+ icon: it.icon,
+ entities: it.entities,
+ endpoint: it.endpoint,
+ proxy: it.proxy,
+ token: it.token,
+ username: it.username,
+ password: it.password,
+ authMethod: it.authMethod,
+ }));
+ };
+
+ const handleRefresh = async (plugin?: string) => {
+ if (plugin) {
+ const res = await getConnection(plugin);
+ setConnections([...connections.filter((cs) => cs.plugin !== plugin),
...transformConnection(res)]);
+ return;
+ }
- const resWithPlugin = res.map((cs, i) =>
- cs.map((it: any) => {
- const { plugin, icon, entities } = allConnections[i];
+ const res = await Promise.all(plugins.map((cs) =>
getConnection(cs.plugin)));
- return {
- ...it,
- plugin,
- icon,
- entities,
- };
- }),
- );
+ setConnections(transformConnection(res.flat()));
+ setLoading(false);
+ };
- setConnections(
- resWithPlugin.flat().map((it) => ({
- unique: `${it.plugin}-${it.id}`,
- status: ConnectionStatusEnum.NULL,
- plugin: it.plugin,
- id: it.id,
- name: it.name,
- icon: it.icon,
- entities: it.entities,
- endpoint: it.endpoint,
- proxy: it.proxy,
- token: it.token,
- username: it.username,
- password: it.password,
- authMethod: it.authMethod,
- })),
+ const handleTest = async (unique: string) => {
+ setConnections((connections) =>
+ connections.map((cs) =>
+ cs.unique === unique
+ ? {
+ ...cs,
+ status: ConnectionStatusEnum.TESTING,
+ }
+ : cs,
+ ),
);
- setLoading(false);
- }, [allConnections]);
+ console.log(connections);
+
+ const connection = connections.find((cs) => cs.unique === unique) as
ConnectionItemType;
+ const status = await testConnection(connection);
+
+ setConnections((connections) =>
+ connections.map((cs) =>
+ cs.unique === unique
+ ? {
+ ...cs,
+ status,
+ }
+ : cs,
+ ),
+ );
+ };
useEffect(() => {
handleRefresh();
- }, [allConnections]);
-
- const handleTest = useCallback(
- async (selectedConnection: ConnectionItemType) => {
- setConnections((connections) =>
- connections.map((cs) =>
- cs.unique === selectedConnection.unique
- ? {
- ...cs,
- status: ConnectionStatusEnum.TESTING,
- }
- : cs,
- ),
- );
-
- const { plugin, endpoint, proxy, token, username, password, authMethod }
= selectedConnection;
-
- let status = ConnectionStatusEnum.OFFLINE;
-
- try {
- const res = await API.testConnection(plugin, {
- endpoint,
- proxy,
- token,
- username,
- password,
- authMethod,
- });
- status = res.success ? ConnectionStatusEnum.ONLINE :
ConnectionStatusEnum.OFFLINE;
- } catch {
- status = ConnectionStatusEnum.OFFLINE;
- }
-
- setConnections((connections) =>
- connections.map((cs) =>
- cs.unique === selectedConnection.unique
- ? {
- ...cs,
- status,
- }
- : cs,
- ),
- );
- },
- [connections],
- );
+ }, []);
return useMemo(
() => ({