You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@devlake.apache.org by li...@apache.org on 2023/05/11 10:06:35 UTC
[incubator-devlake] branch main updated: feat(config-ui): improve the connection detail page (#5158)
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 c488fea71 feat(config-ui): improve the connection detail page (#5158)
c488fea71 is described below
commit c488fea7100d1ec91bdc7edd16dc52760cb84ea1
Author: 青湛 <0x...@gmail.com>
AuthorDate: Thu May 11 18:06:30 2023 +0800
feat(config-ui): improve the connection detail page (#5158)
* feat(config-ui): add global store tips
* feat(config-ui): add props disabledItems in data scope
* feat(config-ui): improve the connection detail page
---
config-ui/src/layouts/base/base.tsx | 196 ++++++++++---------
config-ui/src/layouts/base/styled.ts | 25 ++-
config-ui/src/pages/connection/detail/api.ts | 2 +
config-ui/src/pages/connection/detail/index.tsx | 86 ++++++++-
config-ui/src/pages/connection/detail/styled.ts | 5 +
.../components/data-scope-form-2}/api.ts | 16 +-
.../plugins/components/data-scope-form-2/index.tsx | 215 +++++++++++++++++++++
.../components/data-scope-form-2/styled.ts} | 16 +-
.../components/data-scope-miller-columns/index.tsx | 10 +-
.../plugins/components/data-scope-search/index.tsx | 4 +-
config-ui/src/plugins/components/index.ts | 1 +
.../src/plugins/register/azure/data-scope.tsx | 9 +-
.../src/plugins/register/bitbucket/data-scope.tsx | 9 +-
.../github/components/miller-columns/index.tsx | 9 +-
.../github/components/repo-selector/index.tsx | 6 +-
.../src/plugins/register/github/data-scope.tsx | 17 +-
.../src/plugins/register/gitlab/data-scope.tsx | 8 +
.../jenkins/components/miller-columns/index.tsx | 9 +-
.../src/plugins/register/jenkins/data-scope.tsx | 3 +-
.../jira/components/miller-columns/index.tsx | 9 +-
config-ui/src/plugins/register/jira/data-scope.tsx | 12 +-
.../src/plugins/register/pagerduty/data-scope.tsx | 7 +
.../src/plugins/register/sonarqube/data-scope.tsx | 9 +-
config-ui/src/plugins/register/tapd/data-scope.tsx | 9 +-
.../src/plugins/register/zentao/data-scope.tsx | 14 +-
config-ui/src/store/index.ts | 1 +
.../jira/data-scope.tsx => store/tips/context.tsx} | 37 ++--
config-ui/src/store/{ => tips}/index.ts | 2 +-
28 files changed, 593 insertions(+), 153 deletions(-)
diff --git a/config-ui/src/layouts/base/base.tsx b/config-ui/src/layouts/base/base.tsx
index 5ee3cfa43..8b5d8a069 100644
--- a/config-ui/src/layouts/base/base.tsx
+++ b/config-ui/src/layouts/base/base.tsx
@@ -22,6 +22,7 @@ import { Menu, MenuItem, Tag, Navbar, Intent, Alignment, Button } from '@bluepri
import { PageLoading, Logo, ExternalLink } from '@/components';
import { useRefreshData } from '@/hooks';
+import { TipsContextProvider, TipsContextConsumer } from '@/store';
import { history } from '@/utils/history';
import DashboardIcon from '@/images/icons/dashborad.svg';
@@ -71,99 +72,106 @@ export const BaseLayout = ({ children }: Props) => {
}
return (
- <S.Wrapper>
- <S.Sider>
- <Logo />
- <Menu className="menu">
- {menu.map((it) => {
- const paths = [it.path, ...(it.children ?? []).map((cit) => cit.path)];
- const active = !!paths.find((path) => pathname.includes(path));
- return (
- <MenuItem
- key={it.key}
- className="menu-item"
- text={it.title}
- icon={it.icon}
- active={active}
- onClick={() => handlePushPath(it)}
- >
- {it.children?.map((cit) => (
- <MenuItem
- key={cit.key}
- className="sub-menu-item"
- text={
- <S.SiderMenuItem>
- <span>{cit.title}</span>
- {cit.isBeta && <Tag intent={Intent.WARNING}>beta</Tag>}
- </S.SiderMenuItem>
- }
- icon={cit.icon ?? <img src={cit.iconUrl} width={16} alt="" />}
- active={pathname.includes(cit.path)}
- disabled={cit.disabled}
- onClick={() => handlePushPath(cit)}
- />
- ))}
- </MenuItem>
- );
- })}
- </Menu>
- <div className="copyright">
- <div>Apache 2.0 License</div>
- <div className="version">{data.version}</div>
- </div>
- </S.Sider>
- <S.Main>
- <S.Header>
- <Navbar.Group align={Alignment.RIGHT}>
- <S.DashboardIcon>
- <ExternalLink link={getGrafanaUrl()}>
- <img src={DashboardIcon} alt="dashboards" />
- <span>Dashboards</span>
- </ExternalLink>
- </S.DashboardIcon>
- <Navbar.Divider />
- <a href="https://devlake.apache.org/docs/Configuration/Tutorial" rel="noreferrer" target="_blank">
- <img src={FileIcon} alt="documents" />
- <span>Docs</span>
- </a>
- <Navbar.Divider />
- <ExternalLink link="/api/swagger/index.html">
- <img src={APIIcon} alt="api" />
- <span>API</span>
- </ExternalLink>
- <Navbar.Divider />
- <a
- href="https://github.com/apache/incubator-devlake"
- rel="noreferrer"
- target="_blank"
- className="navIconLink"
- >
- <img src={GitHubIcon} alt="github" />
- <span>GitHub</span>
- </a>
- <Navbar.Divider />
- <a
- href="https://join.slack.com/t/devlake-io/shared_invite/zt-17b6vuvps-x98pqseoUagM7EAmKC82xQ"
- rel="noreferrer"
- target="_blank"
- >
- <img src={SlackIcon} alt="slack" />
- <span>Slack</span>
- </a>
- {token && (
- <>
- <Navbar.Divider />
- <Button small intent={Intent.NONE} onClick={handleSignOut}>
- Sign Out
- </Button>
- </>
- )}
- </Navbar.Group>
- </S.Header>
- <S.Inner>
- <S.Content>{children}</S.Content>
- </S.Inner>
- </S.Main>
- </S.Wrapper>
+ <TipsContextProvider>
+ <TipsContextConsumer>
+ {({ text }) => (
+ <S.Wrapper>
+ <S.Sider>
+ <Logo />
+ <Menu className="menu">
+ {menu.map((it) => {
+ const paths = [it.path, ...(it.children ?? []).map((cit) => cit.path)];
+ const active = !!paths.find((path) => pathname.includes(path));
+ return (
+ <MenuItem
+ key={it.key}
+ className="menu-item"
+ text={it.title}
+ icon={it.icon}
+ active={active}
+ onClick={() => handlePushPath(it)}
+ >
+ {it.children?.map((cit) => (
+ <MenuItem
+ key={cit.key}
+ className="sub-menu-item"
+ text={
+ <S.SiderMenuItem>
+ <span>{cit.title}</span>
+ {cit.isBeta && <Tag intent={Intent.WARNING}>beta</Tag>}
+ </S.SiderMenuItem>
+ }
+ icon={cit.icon ?? <img src={cit.iconUrl} width={16} alt="" />}
+ active={pathname.includes(cit.path)}
+ disabled={cit.disabled}
+ onClick={() => handlePushPath(cit)}
+ />
+ ))}
+ </MenuItem>
+ );
+ })}
+ </Menu>
+ <div className="copyright">
+ <div>Apache 2.0 License</div>
+ <div className="version">{data.version}</div>
+ </div>
+ </S.Sider>
+ <S.Main>
+ <S.Header>
+ <Navbar.Group align={Alignment.RIGHT}>
+ <S.DashboardIcon>
+ <ExternalLink link={getGrafanaUrl()}>
+ <img src={DashboardIcon} alt="dashboards" />
+ <span>Dashboards</span>
+ </ExternalLink>
+ </S.DashboardIcon>
+ <Navbar.Divider />
+ <a href="https://devlake.apache.org/docs/Configuration/Tutorial" rel="noreferrer" target="_blank">
+ <img src={FileIcon} alt="documents" />
+ <span>Docs</span>
+ </a>
+ <Navbar.Divider />
+ <ExternalLink link="/api/swagger/index.html">
+ <img src={APIIcon} alt="api" />
+ <span>API</span>
+ </ExternalLink>
+ <Navbar.Divider />
+ <a
+ href="https://github.com/apache/incubator-devlake"
+ rel="noreferrer"
+ target="_blank"
+ className="navIconLink"
+ >
+ <img src={GitHubIcon} alt="github" />
+ <span>GitHub</span>
+ </a>
+ <Navbar.Divider />
+ <a
+ href="https://join.slack.com/t/devlake-io/shared_invite/zt-17b6vuvps-x98pqseoUagM7EAmKC82xQ"
+ rel="noreferrer"
+ target="_blank"
+ >
+ <img src={SlackIcon} alt="slack" />
+ <span>Slack</span>
+ </a>
+ {token && (
+ <>
+ <Navbar.Divider />
+ <Button small intent={Intent.NONE} onClick={handleSignOut}>
+ Sign Out
+ </Button>
+ </>
+ )}
+ </Navbar.Group>
+ </S.Header>
+ <S.Inner>
+ <S.Content>{children}</S.Content>
+ </S.Inner>
+ {text && <S.Tips>{text}</S.Tips>}
+ </S.Main>
+ </S.Wrapper>
+ )}
+ </TipsContextConsumer>
+ </TipsContextProvider>
);
};
diff --git a/config-ui/src/layouts/base/styled.ts b/config-ui/src/layouts/base/styled.ts
index c3303e89a..f04f314ec 100644
--- a/config-ui/src/layouts/base/styled.ts
+++ b/config-ui/src/layouts/base/styled.ts
@@ -82,14 +82,6 @@ export const Sider = styled.div`
}
`;
-export const Main = styled.div`
- display: flex;
- flex-direction: column;
- flex: auto;
- height: 100vh;
- overflow: hidden;
-`;
-
export const Header = styled(Navbar)`
flex: 0 0 50px;
background-color: #f9f9fa;
@@ -110,6 +102,14 @@ export const Header = styled(Navbar)`
}
`;
+export const Main = styled.div`
+ display: flex;
+ flex-direction: column;
+ flex: auto;
+ height: 100vh;
+ overflow: hidden;
+`;
+
export const Inner = styled.div`
flex: auto;
margin-top: 24px;
@@ -124,6 +124,15 @@ export const Content = styled.div`
min-width: 900px;
`;
+export const Tips = styled.div`
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 14px 0;
+ color: #fff;
+ background-color: #3c5088;
+`;
+
export const SiderMenuItem = styled.div`
display: flex;
align-items: center;
diff --git a/config-ui/src/pages/connection/detail/api.ts b/config-ui/src/pages/connection/detail/api.ts
index 3d7df8db5..c86588cc4 100644
--- a/config-ui/src/pages/connection/detail/api.ts
+++ b/config-ui/src/pages/connection/detail/api.ts
@@ -20,3 +20,5 @@ import { request } from '@/utils';
export const deleteConnection = (plugin: string, id: ID) =>
request(`/plugins/${plugin}/connections/${id}`, { method: 'delete' });
+
+export const getDataScope = (plugin: string, id: ID) => request(`/plugins/${plugin}/connections/${id}/scopes`);
diff --git a/config-ui/src/pages/connection/detail/index.tsx b/config-ui/src/pages/connection/detail/index.tsx
index 4ce9a8dc3..743b08174 100644
--- a/config-ui/src/pages/connection/detail/index.tsx
+++ b/config-ui/src/pages/connection/detail/index.tsx
@@ -20,11 +20,12 @@ 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 { PageHeader, Dialog, IconButton, Table } from '@/components';
import { transformEntities } from '@/config';
-import { ConnectionForm } from '@/plugins';
+import { useRefreshData } from '@/hooks';
+import { ConnectionForm, DataScopeForm2 } from '@/plugins';
import type { ConnectionItemType } from '@/store';
-import { ConnectionContextProvider, useConnection, ConnectionStatus } from '@/store';
+import { ConnectionContextProvider, useConnection, ConnectionStatus, useTips } from '@/store';
import { operator } from '@/utils';
import * as API from './api';
@@ -36,15 +37,35 @@ interface Props {
}
const ConnectionDetail = ({ plugin, id }: Props) => {
- const [type, setType] = useState<'deleteConnection' | 'updateConnection'>();
+ const [type, setType] = useState<'deleteConnection' | 'updateConnection' | 'createDataScope'>();
const [operating, setOperating] = useState(false);
+ const [version, setVersion] = useState(1);
const history = useHistory();
const { connections, onRefresh, onTest } = useConnection();
+ const { setText } = useTips();
+ const { ready, data } = useRefreshData(() => API.getDataScope(plugin, id), [version]);
+
const { unique, status, name, icon, entities } = connections.find(
(cs) => cs.unique === `${plugin}-${id}`,
) as ConnectionItemType;
+ const handleHideDialog = () => {
+ setType(undefined);
+ };
+
+ const handleShowTips = () => {
+ setText(
+ <div>
+ <Icon icon="warning-sign" style={{ marginRight: 8 }} color="#F4BE55" />
+ <span>
+ The transformation of certain data scope has been updated. If you would like to re-transform the data in the
+ related project(s), please go to the Project page and do so.
+ </span>
+ </div>,
+ );
+ };
+
const handleShowDeleteDialog = () => {
setType('deleteConnection');
};
@@ -65,12 +86,18 @@ const ConnectionDetail = ({ plugin, id }: Props) => {
};
const handleUpdate = () => {
- setType(undefined);
onRefresh(plugin);
+ handleHideDialog();
};
- const handleHideDialog = () => {
- setType(undefined);
+ const handleShowCreateDataScopeDialog = () => {
+ setType('createDataScope');
+ };
+
+ const handleCreateDataScope = () => {
+ setVersion((v) => v + 1);
+ handleShowTips();
+ handleHideDialog();
};
return (
@@ -102,6 +129,25 @@ const ConnectionDetail = ({ plugin, id }: Props) => {
</span>
</div>
</div>
+ <div className="action">
+ <Button intent={Intent.PRIMARY} icon="add" text="Add Data Scope" onClick={handleShowCreateDataScopeDialog} />
+ </div>
+ <Table
+ loading={!ready}
+ columns={[
+ {
+ title: 'Data Scope',
+ dataIndex: 'name',
+ key: 'name',
+ },
+ ]}
+ dataSource={data}
+ noData={{
+ text: 'Add data to this connection.',
+ btnText: 'Add Data Scope',
+ onCreate: handleShowCreateDataScopeDialog,
+ }}
+ />
</S.Wrapper>
{type === 'deleteConnection' && (
<Dialog
@@ -123,9 +169,9 @@ const ConnectionDetail = ({ plugin, id }: Props) => {
)}
{type === 'updateConnection' && (
<Dialog
+ isOpen
style={{ width: 820 }}
footer={null}
- isOpen
title={
<S.DialogTitle>
<img src={icon} alt="" />
@@ -137,6 +183,28 @@ const ConnectionDetail = ({ plugin, id }: Props) => {
<ConnectionForm plugin={plugin} connectionId={id} onSuccess={handleUpdate} />
</Dialog>
)}
+ {type === 'createDataScope' && (
+ <Dialog
+ isOpen
+ style={{ width: 820 }}
+ footer={null}
+ title={
+ <S.DialogTitle>
+ <img src={icon} alt="" />
+ <span>Add Data Scope: {name}</span>
+ </S.DialogTitle>
+ }
+ onCancel={handleHideDialog}
+ >
+ <DataScopeForm2
+ plugin={plugin}
+ connectionId={id}
+ disabledScope={data}
+ onCancel={handleHideDialog}
+ onSubmit={handleCreateDataScope}
+ />
+ </Dialog>
+ )}
</PageHeader>
);
};
@@ -146,7 +214,7 @@ export const ConnectionDetailPage = () => {
return (
<ConnectionContextProvider plugin={plugin}>
- <ConnectionDetail plugin={plugin} id={id} />
+ <ConnectionDetail plugin={plugin} id={+id} />
</ConnectionContextProvider>
);
};
diff --git a/config-ui/src/pages/connection/detail/styled.ts b/config-ui/src/pages/connection/detail/styled.ts
index e8b6e0212..4a9225132 100644
--- a/config-ui/src/pages/connection/detail/styled.ts
+++ b/config-ui/src/pages/connection/detail/styled.ts
@@ -33,6 +33,11 @@ export const Wrapper = styled.div`
span
}
}
+
+ .action {
+ margin-top: 36px;
+ margin-bottom: 24px;
+ }
`;
export const DialogTitle = styled.div`
diff --git a/config-ui/src/pages/connection/detail/api.ts b/config-ui/src/plugins/components/data-scope-form-2/api.ts
similarity index 58%
copy from config-ui/src/pages/connection/detail/api.ts
copy to config-ui/src/plugins/components/data-scope-form-2/api.ts
index 3d7df8db5..19abb110d 100644
--- a/config-ui/src/pages/connection/detail/api.ts
+++ b/config-ui/src/plugins/components/data-scope-form-2/api.ts
@@ -18,5 +18,17 @@
import { request } from '@/utils';
-export const deleteConnection = (plugin: string, id: ID) =>
- request(`/plugins/${plugin}/connections/${id}`, { method: 'delete' });
+export const getDataScope = (plugin: string, connectionId: ID, scopeId: string) =>
+ request(`/plugins/${plugin}/connections/${connectionId}/scopes/${scopeId}`);
+
+export const updateDataScope = (plugin: string, connectionId: ID, payload: any) =>
+ request(`/plugins/${plugin}/connections/${connectionId}/scopes`, {
+ method: 'put',
+ data: payload,
+ });
+
+export const updateDataScopeWithType = (plugin: string, connectionId: ID, type: string, payload: any) =>
+ request(`/plugins/${plugin}/connections/${connectionId}/${type}/scopes`, {
+ method: 'put',
+ data: payload,
+ });
diff --git a/config-ui/src/plugins/components/data-scope-form-2/index.tsx b/config-ui/src/plugins/components/data-scope-form-2/index.tsx
new file mode 100644
index 000000000..4d64dc8df
--- /dev/null
+++ b/config-ui/src/plugins/components/data-scope-form-2/index.tsx
@@ -0,0 +1,215 @@
+/*
+ * 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 { useMemo, useState } from 'react';
+import { Button, Intent } from '@blueprintjs/core';
+
+import { getPluginId } from '@/plugins';
+
+import { GitHubDataScope } from '@/plugins/register/github';
+import { JiraDataScope } from '@/plugins/register/jira';
+import { GitLabDataScope } from '@/plugins/register/gitlab';
+import { JenkinsDataScope } from '@/plugins/register/jenkins';
+import { BitbucketDataScope } from '@/plugins/register/bitbucket';
+import { AzureDataScope } from '@/plugins/register/azure';
+import { SonarQubeDataScope } from '@/plugins/register/sonarqube';
+import { PagerDutyDataScope } from '@/plugins/register/pagerduty';
+import { ZentaoDataScope } from '@/plugins/register/zentao';
+
+import * as API from './api';
+import * as S from './styled';
+import { TapdDataScope } from '@/plugins/register/tapd';
+
+interface Props {
+ plugin: string;
+ connectionId: ID;
+ disabledScope?: any[];
+ cancelBtnProps?: {
+ text?: string;
+ };
+ submitBtnProps?: {
+ text?: string;
+ };
+ onCancel?: () => void;
+ onSubmit?: (origin: any) => void;
+}
+
+export const DataScopeForm2 = ({
+ plugin,
+ connectionId,
+ disabledScope,
+ onSubmit,
+ onCancel,
+ cancelBtnProps,
+ submitBtnProps,
+}: Props) => {
+ const [operating, setOperating] = useState(false);
+ const [scope, setScope] = useState<any>([]);
+
+ const error = useMemo(() => (!scope.length ? 'No Data Scope is Selected' : ''), [scope]);
+
+ const getDataScope = async (scope: any) => {
+ try {
+ const res = await API.getDataScope(plugin, connectionId, scope[getPluginId(plugin)]);
+ return {
+ ...scope,
+ transformationRuleId: res.transformationRuleId,
+ };
+ } catch {
+ return scope;
+ }
+ };
+
+ const handleSubmit = async () => {
+ setOperating(true);
+ try {
+ const data = await Promise.all(scope.map((sc: any) => getDataScope(sc)));
+ const res =
+ plugin === 'zentao'
+ ? [
+ ...(await API.updateDataScopeWithType(plugin, connectionId, 'product', {
+ data: data.filter((s) => s.type !== 'project'),
+ })),
+ ...(await API.updateDataScopeWithType(plugin, connectionId, 'project', {
+ data: data.filter((s) => s.type === 'project'),
+ })),
+ ]
+ : await API.updateDataScope(plugin, connectionId, {
+ data,
+ });
+
+ onSubmit?.(res);
+ } finally {
+ setOperating(false);
+ }
+ };
+
+ return (
+ <S.Wrapper>
+ {plugin === 'github' && (
+ <GitHubDataScope
+ connectionId={connectionId}
+ disabledItems={disabledScope}
+ selectedItems={scope}
+ onChangeItems={setScope}
+ />
+ )}
+
+ {plugin === 'jira' && (
+ <JiraDataScope
+ connectionId={connectionId}
+ disabledItems={disabledScope}
+ selectedItems={scope}
+ onChangeItems={setScope}
+ />
+ )}
+
+ {plugin === 'gitlab' && (
+ <GitLabDataScope
+ connectionId={connectionId}
+ disabledItems={disabledScope}
+ selectedItems={scope}
+ onChangeItems={setScope}
+ />
+ )}
+
+ {plugin === 'jenkins' && (
+ <JenkinsDataScope
+ connectionId={connectionId}
+ disabledItems={disabledScope}
+ selectedItems={scope}
+ onChangeItems={setScope}
+ />
+ )}
+
+ {plugin === 'bitbucket' && (
+ <BitbucketDataScope
+ disabledItems={disabledScope}
+ connectionId={connectionId}
+ selectedItems={scope}
+ onChangeItems={setScope}
+ />
+ )}
+
+ {plugin === 'azuredevops' && (
+ <AzureDataScope
+ disabledItems={disabledScope}
+ connectionId={connectionId}
+ selectedItems={scope}
+ onChangeItems={setScope}
+ />
+ )}
+
+ {plugin === 'sonarqube' && (
+ <SonarQubeDataScope
+ disabledItems={disabledScope}
+ connectionId={connectionId}
+ selectedItems={scope}
+ onChangeItems={setScope}
+ />
+ )}
+
+ {plugin === 'pagerduty' && (
+ <PagerDutyDataScope
+ connectionId={connectionId}
+ disabledItems={disabledScope}
+ selectedItems={scope}
+ onChangeItems={setScope}
+ />
+ )}
+
+ {plugin === 'tapd' && (
+ <TapdDataScope
+ connectionId={connectionId}
+ disabledItems={disabledScope}
+ selectedItems={scope}
+ onChangeItems={setScope}
+ />
+ )}
+
+ {plugin === 'zentao' && (
+ <ZentaoDataScope
+ connectionId={connectionId}
+ disabledItems={disabledScope}
+ selectedItems={scope}
+ onChangeItems={setScope}
+ />
+ )}
+
+ <div className="action">
+ <Button
+ outlined
+ intent={Intent.PRIMARY}
+ text="Cancel"
+ {...cancelBtnProps}
+ disabled={operating}
+ onClick={onCancel}
+ />
+ <Button
+ outlined
+ intent={Intent.PRIMARY}
+ text="Save"
+ {...submitBtnProps}
+ loading={operating}
+ disabled={!!error}
+ onClick={handleSubmit}
+ />
+ </div>
+ </S.Wrapper>
+ );
+};
diff --git a/config-ui/src/pages/connection/detail/api.ts b/config-ui/src/plugins/components/data-scope-form-2/styled.ts
similarity index 76%
copy from config-ui/src/pages/connection/detail/api.ts
copy to config-ui/src/plugins/components/data-scope-form-2/styled.ts
index 3d7df8db5..e10dd267e 100644
--- a/config-ui/src/pages/connection/detail/api.ts
+++ b/config-ui/src/plugins/components/data-scope-form-2/styled.ts
@@ -16,7 +16,17 @@
*
*/
-import { request } from '@/utils';
+import styled from 'styled-components';
-export const deleteConnection = (plugin: string, id: ID) =>
- request(`/plugins/${plugin}/connections/${id}`, { method: 'delete' });
+export const Wrapper = styled.div`
+ .action {
+ display: flex;
+ align-items: center;
+ justify-content: end;
+ margin-top: 36px;
+
+ .bp4-button + .bp4-button {
+ margin-left: 8px;
+ }
+ }
+`;
diff --git a/config-ui/src/plugins/components/data-scope-miller-columns/index.tsx b/config-ui/src/plugins/components/data-scope-miller-columns/index.tsx
index 3f380011c..0fe73014b 100644
--- a/config-ui/src/plugins/components/data-scope-miller-columns/index.tsx
+++ b/config-ui/src/plugins/components/data-scope-miller-columns/index.tsx
@@ -31,6 +31,7 @@ interface Props extends Pick<MillerColumnsSelectProps<ExtraType>, 'columnCount'>
title?: string;
plugin: string;
connectionId: ID;
+ disabledItems?: any[];
selectedItems?: any[];
pageToken?: string;
onChangeItems?: (selectedItems: any[]) => void;
@@ -40,6 +41,7 @@ export const DataScopeMillerColumns = ({
title,
plugin,
connectionId,
+ disabledItems,
selectedItems,
onChangeItems,
pageToken,
@@ -47,13 +49,18 @@ export const DataScopeMillerColumns = ({
}: Props) => {
const [items, setItems] = useState<McsItem<ExtraType>[]>([]);
const [selectedIds, setSelectedIds] = useState<ID[]>([]);
+ const [disabledIds, setDisabledIds] = useState<ID[]>([]);
const [loadedIds, setLoadedIds] = useState<ID[]>([]);
const [nextTokenMap, setNextTokenMap] = useState<Record<ID, string>>({});
useEffect(() => {
- setSelectedIds((selectedItems ?? []).map((it: any) => it.id));
+ setSelectedIds((selectedItems ?? []).map((it) => it.id));
}, [selectedItems]);
+ useEffect(() => {
+ setDisabledIds((disabledItems ?? []).map((it) => it.id));
+ }, [disabledItems]);
+
const getItems = async (groupId: ID | null, currentPageToken?: string) => {
if (!currentPageToken) {
currentPageToken = pageToken;
@@ -122,6 +129,7 @@ export const DataScopeMillerColumns = ({
columnHeight={300}
renderTitle={renderTitle}
renderLoading={renderLoading}
+ disabledIds={disabledIds}
selectedIds={selectedIds}
onSelectItemIds={handleChangeItems}
{...props}
diff --git a/config-ui/src/plugins/components/data-scope-search/index.tsx b/config-ui/src/plugins/components/data-scope-search/index.tsx
index 586b2338d..3d2aa7e8a 100644
--- a/config-ui/src/plugins/components/data-scope-search/index.tsx
+++ b/config-ui/src/plugins/components/data-scope-search/index.tsx
@@ -26,11 +26,12 @@ import * as API from './api';
interface Props {
plugin: string;
connectionId: ID;
+ disabledItems?: any[];
selectedItems?: any[];
onChangeItems?: (selectedItems: any[]) => void;
}
-export const DataScopeSearch = ({ plugin, connectionId, selectedItems, onChangeItems }: Props) => {
+export const DataScopeSearch = ({ plugin, connectionId, disabledItems, selectedItems, onChangeItems }: Props) => {
const [loading, setLoading] = useState(false);
const [items, setItems] = useState<ItemType[]>([]);
const [search, setSearch] = useState('');
@@ -68,6 +69,7 @@ export const DataScopeSearch = ({ plugin, connectionId, selectedItems, onChangeI
items={items}
getKey={getKey}
getName={getName}
+ disabledItems={disabledItems}
selectedItems={selectedItems}
onChangeItems={handleChangeItems}
loading={loading}
diff --git a/config-ui/src/plugins/components/index.ts b/config-ui/src/plugins/components/index.ts
index 307482460..6f45b86b7 100644
--- a/config-ui/src/plugins/components/index.ts
+++ b/config-ui/src/plugins/components/index.ts
@@ -19,6 +19,7 @@
export * from './connection-form';
export * from './data-scope';
export * from './data-scope-form';
+export * from './data-scope-form-2';
export * from './data-scope-miller-columns';
export * from './data-scope-search';
export * from './transformation';
diff --git a/config-ui/src/plugins/register/azure/data-scope.tsx b/config-ui/src/plugins/register/azure/data-scope.tsx
index 41393db42..7659d7425 100644
--- a/config-ui/src/plugins/register/azure/data-scope.tsx
+++ b/config-ui/src/plugins/register/azure/data-scope.tsx
@@ -16,7 +16,7 @@
*
*/
-import React, { useMemo } from 'react';
+import { useMemo } from 'react';
import { DataScopeMillerColumns } from '@/plugins';
@@ -24,6 +24,7 @@ import type { AzureScopeType } from './types';
interface Props {
connectionId: ID;
+ disabledItems?: AzureScopeType[];
selectedItems: AzureScopeType[];
onChangeItems: (selectedItems: AzureScopeType[]) => void;
}
@@ -34,6 +35,11 @@ export const AzureDataScope = ({ connectionId, onChangeItems, ...props }: Props)
[props.selectedItems],
);
+ const disabledItems = useMemo(
+ () => (props.disabledItems ?? []).map((it) => ({ id: `${it.id}`, name: it.name, data: it })),
+ [props.disabledItems],
+ );
+
return (
<>
<h4>Add Repositories by Selecting from the Directory</h4>
@@ -41,6 +47,7 @@ export const AzureDataScope = ({ connectionId, onChangeItems, ...props }: Props)
<DataScopeMillerColumns
plugin="azuredevops"
connectionId={connectionId}
+ disabledItems={disabledItems}
selectedItems={selectedItems}
onChangeItems={onChangeItems}
/>
diff --git a/config-ui/src/plugins/register/bitbucket/data-scope.tsx b/config-ui/src/plugins/register/bitbucket/data-scope.tsx
index 9fd8a1163..d0130b213 100644
--- a/config-ui/src/plugins/register/bitbucket/data-scope.tsx
+++ b/config-ui/src/plugins/register/bitbucket/data-scope.tsx
@@ -16,7 +16,7 @@
*
*/
-import React, { useMemo } from 'react';
+import { useMemo } from 'react';
import { DataScopeMillerColumns } from '@/plugins';
@@ -24,6 +24,7 @@ import type { ScopeItemType } from './types';
interface Props {
connectionId: ID;
+ disabledItems?: ScopeItemType[];
selectedItems: ScopeItemType[];
onChangeItems: (selectedItems: ScopeItemType[]) => void;
}
@@ -34,6 +35,11 @@ export const BitbucketDataScope = ({ connectionId, onChangeItems, ...props }: Pr
[props.selectedItems],
);
+ const disabledItems = useMemo(
+ () => (props.disabledItems ?? []).map((it) => ({ id: `${it.bitbucketId}`, name: it.name, data: it })),
+ [props.disabledItems],
+ );
+
return (
<>
<h3>Repositories *</h3>
@@ -41,6 +47,7 @@ export const BitbucketDataScope = ({ connectionId, onChangeItems, ...props }: Pr
<DataScopeMillerColumns
plugin="bitbucket"
connectionId={connectionId}
+ disabledItems={disabledItems}
selectedItems={selectedItems}
onChangeItems={onChangeItems}
/>
diff --git a/config-ui/src/plugins/register/github/components/miller-columns/index.tsx b/config-ui/src/plugins/register/github/components/miller-columns/index.tsx
index 733793a2c..842de51c9 100644
--- a/config-ui/src/plugins/register/github/components/miller-columns/index.tsx
+++ b/config-ui/src/plugins/register/github/components/miller-columns/index.tsx
@@ -29,12 +29,14 @@ import { useMillerColumns } from './use-miller-columns';
import * as S from './styled';
interface Props extends UseMillerColumnsProps {
+ disabledItems?: ScopeItemType[];
selectedItems: ScopeItemType[];
onChangeItems: (selectedItems: ScopeItemType[]) => void;
}
-export const MillerColumns = ({ connectionId, selectedItems, onChangeItems }: Props) => {
+export const MillerColumns = ({ connectionId, disabledItems, selectedItems, onChangeItems }: Props) => {
const [selectedIds, setSelectedIds] = useState<McsID[]>([]);
+ const [disabledIds, setDisabledIds] = useState<McsID[]>([]);
const { items, getHasMore, onExpand, onScroll } = useMillerColumns({
connectionId,
@@ -44,6 +46,10 @@ export const MillerColumns = ({ connectionId, selectedItems, onChangeItems }: Pr
setSelectedIds(selectedItems.map((it) => it.githubId));
}, [selectedItems]);
+ useEffect(() => {
+ setDisabledIds((disabledItems ?? []).map((it) => it.githubId));
+ }, [disabledIds]);
+
const handleChangeItems = (selectedIds: McsID[]) => {
const result = selectedIds.map((id) => {
const selectedItem = selectedItems.find((it) => it.githubId === id);
@@ -86,6 +92,7 @@ export const MillerColumns = ({ connectionId, selectedItems, onChangeItems }: Pr
columnHeight={300}
renderTitle={renderTitle}
renderLoading={renderLoading}
+ disabledIds={disabledIds}
selectedIds={selectedIds}
onSelectItemIds={handleChangeItems}
/>
diff --git a/config-ui/src/plugins/register/github/components/repo-selector/index.tsx b/config-ui/src/plugins/register/github/components/repo-selector/index.tsx
index ce1d697b2..f4da815f4 100644
--- a/config-ui/src/plugins/register/github/components/repo-selector/index.tsx
+++ b/config-ui/src/plugins/register/github/components/repo-selector/index.tsx
@@ -16,8 +16,6 @@
*
*/
-import React from 'react';
-
import { MultiSelector } from '@/components';
import { ScopeItemType } from '../../types';
@@ -25,11 +23,12 @@ import { ScopeItemType } from '../../types';
import { useRepoSelector, UseRepoSelectorProps } from './use-repo-selector';
interface Props extends UseRepoSelectorProps {
+ disabledItems?: ScopeItemType[];
selectedItems: ScopeItemType[];
onChangeItems: (selectedItems: ScopeItemType[]) => void;
}
-export const RepoSelector = ({ selectedItems, onChangeItems, ...props }: Props) => {
+export const RepoSelector = ({ disabledItems, selectedItems, onChangeItems, ...props }: Props) => {
const { loading, items, onSearch } = useRepoSelector(props);
return (
@@ -38,6 +37,7 @@ export const RepoSelector = ({ selectedItems, onChangeItems, ...props }: Props)
items={items}
getKey={(it) => it.githubId}
getName={(it) => it.name}
+ disabledItems={disabledItems}
selectedItems={selectedItems}
onChangeItems={onChangeItems}
loading={loading}
diff --git a/config-ui/src/plugins/register/github/data-scope.tsx b/config-ui/src/plugins/register/github/data-scope.tsx
index d63c71bad..7604c3cea 100644
--- a/config-ui/src/plugins/register/github/data-scope.tsx
+++ b/config-ui/src/plugins/register/github/data-scope.tsx
@@ -22,19 +22,30 @@ import * as S from './styled';
interface Props {
connectionId: ID;
+ disabledItems?: ScopeItemType[];
selectedItems: ScopeItemType[];
onChangeItems: (selectedItems: ScopeItemType[]) => void;
}
-export const GitHubDataScope = ({ connectionId, selectedItems, onChangeItems }: Props) => {
+export const GitHubDataScope = ({ connectionId, disabledItems, selectedItems, onChangeItems }: Props) => {
return (
<S.DataScope>
<h3>Repositories *</h3>
<p>Select the repositories you would like to sync.</p>
- <MillerColumns connectionId={connectionId} selectedItems={selectedItems} onChangeItems={onChangeItems} />
+ <MillerColumns
+ connectionId={connectionId}
+ disabledItems={disabledItems}
+ selectedItems={selectedItems}
+ onChangeItems={onChangeItems}
+ />
<h4>Add repositories outside of your organizations</h4>
<p>Search for repositories and add to them</p>
- <RepoSelector connectionId={connectionId} selectedItems={selectedItems} onChangeItems={onChangeItems} />
+ <RepoSelector
+ disabledItems={disabledItems}
+ connectionId={connectionId}
+ selectedItems={selectedItems}
+ onChangeItems={onChangeItems}
+ />
</S.DataScope>
);
};
diff --git a/config-ui/src/plugins/register/gitlab/data-scope.tsx b/config-ui/src/plugins/register/gitlab/data-scope.tsx
index f3a8b63e5..13da84c89 100644
--- a/config-ui/src/plugins/register/gitlab/data-scope.tsx
+++ b/config-ui/src/plugins/register/gitlab/data-scope.tsx
@@ -25,6 +25,7 @@ import * as S from './styled';
interface Props {
connectionId: ID;
+ disabledItems?: ScopeItemType[];
selectedItems: ScopeItemType[];
onChangeItems: (selectedItems: ScopeItemType[]) => void;
}
@@ -35,6 +36,11 @@ export const GitLabDataScope = ({ connectionId, onChangeItems, ...props }: Props
[props.selectedItems],
);
+ const disabledItems = useMemo(
+ () => (props.disabledItems ?? []).map((it) => ({ id: `${it.gitlabId}`, name: it.name, data: it })),
+ [props.disabledItems],
+ );
+
return (
<S.DataScope>
<h3>Projects *</h3>
@@ -43,6 +49,7 @@ export const GitLabDataScope = ({ connectionId, onChangeItems, ...props }: Props
title="Subgroups/Projects"
plugin="gitlab"
connectionId={connectionId}
+ disabledItems={disabledItems}
selectedItems={selectedItems}
onChangeItems={onChangeItems}
/>
@@ -51,6 +58,7 @@ export const GitLabDataScope = ({ connectionId, onChangeItems, ...props }: Props
<DataScopeSearch
plugin="gitlab"
connectionId={connectionId}
+ disabledItems={disabledItems}
selectedItems={selectedItems}
onChangeItems={onChangeItems}
/>
diff --git a/config-ui/src/plugins/register/jenkins/components/miller-columns/index.tsx b/config-ui/src/plugins/register/jenkins/components/miller-columns/index.tsx
index ca3827e05..9a934992e 100644
--- a/config-ui/src/plugins/register/jenkins/components/miller-columns/index.tsx
+++ b/config-ui/src/plugins/register/jenkins/components/miller-columns/index.tsx
@@ -28,12 +28,14 @@ import type { UseMillerColumnsProps, ExtraType } from './use-miller-columns';
import { useMillerColumns } from './use-miller-columns';
interface Props extends UseMillerColumnsProps {
+ disabledItems?: ScopeItemType[];
selectedItems: ScopeItemType[];
onChangeItems: (selectedItems: ScopeItemType[]) => void;
}
-export const MillerColumns = ({ connectionId, selectedItems, onChangeItems }: Props) => {
+export const MillerColumns = ({ connectionId, disabledItems, selectedItems, onChangeItems }: Props) => {
const [selectedIds, setSelectedIds] = useState<ID[]>([]);
+ const [disabledIds, setDisabledIds] = useState<ID[]>([]);
const { items, getHasMore, onExpand } = useMillerColumns({
connectionId,
@@ -43,6 +45,10 @@ export const MillerColumns = ({ connectionId, selectedItems, onChangeItems }: Pr
setSelectedIds(selectedItems.map((it) => it.jobFullName));
}, [selectedItems]);
+ useEffect(() => {
+ setDisabledIds((disabledItems ?? []).map((it) => it.jobFullName));
+ }, [disabledItems]);
+
const handleChangeItems = (selectedIds: ID[]) => {
const result = selectedIds.map((id) => {
const selectedItem = selectedItems.find((it) => it.jobFullName === id);
@@ -75,6 +81,7 @@ export const MillerColumns = ({ connectionId, selectedItems, onChangeItems }: Pr
columnHeight={300}
getHasMore={getHasMore}
renderLoading={renderLoading}
+ disabledIds={disabledIds}
selectedIds={selectedIds}
onSelectItemIds={handleChangeItems}
/>
diff --git a/config-ui/src/plugins/register/jenkins/data-scope.tsx b/config-ui/src/plugins/register/jenkins/data-scope.tsx
index 615447ebf..bf8322840 100644
--- a/config-ui/src/plugins/register/jenkins/data-scope.tsx
+++ b/config-ui/src/plugins/register/jenkins/data-scope.tsx
@@ -22,11 +22,12 @@ import { MillerColumns } from './components';
interface Props {
connectionId: ID;
+ disabledItems?: ScopeItemType[];
selectedItems: ScopeItemType[];
onChangeItems: (selectedItems: ScopeItemType[]) => void;
}
-export const JenkinsDataScope = ({ connectionId, selectedItems, onChangeItems }: Props) => {
+export const JenkinsDataScope = ({ connectionId, disabledItems, selectedItems, onChangeItems }: Props) => {
return (
<>
<h3>Jobs *</h3>
diff --git a/config-ui/src/plugins/register/jira/components/miller-columns/index.tsx b/config-ui/src/plugins/register/jira/components/miller-columns/index.tsx
index 7bc1a7a70..d3bf209a2 100644
--- a/config-ui/src/plugins/register/jira/components/miller-columns/index.tsx
+++ b/config-ui/src/plugins/register/jira/components/miller-columns/index.tsx
@@ -28,12 +28,14 @@ import type { UseMillerColumnsProps } from './use-miller-columns';
import { useMillerColumns } from './use-miller-columns';
interface Props extends UseMillerColumnsProps {
+ disabledItems?: ScopeItemType[];
selectedItems: ScopeItemType[];
onChangeItems: (selectedItems: ScopeItemType[]) => void;
}
-export const MillerColumns = ({ connectionId, selectedItems, onChangeItems }: Props) => {
+export const MillerColumns = ({ connectionId, disabledItems, selectedItems, onChangeItems }: Props) => {
const [selectedIds, setSelectedIds] = useState<ID[]>([]);
+ const [disabledIds, setDisabledIds] = useState<ID[]>([]);
const { items, getHasMore, onScroll } = useMillerColumns({
connectionId,
@@ -43,6 +45,10 @@ export const MillerColumns = ({ connectionId, selectedItems, onChangeItems }: Pr
setSelectedIds(selectedItems.map((it) => it.boardId));
}, [selectedItems]);
+ useEffect(() => {
+ setDisabledIds((disabledItems ?? []).map((it) => it.boardId));
+ }, [disabledItems]);
+
const handleChangeItems = (selectedIds: ID[]) => {
const result = selectedIds.map((id) => {
const selectedItem = selectedItems.find((it) => it.boardId === id);
@@ -76,6 +82,7 @@ export const MillerColumns = ({ connectionId, selectedItems, onChangeItems }: Pr
columnCount={1}
columnHeight={300}
renderLoading={renderLoading}
+ disabledIds={disabledIds}
selectedIds={selectedIds}
onSelectItemIds={handleChangeItems}
/>
diff --git a/config-ui/src/plugins/register/jira/data-scope.tsx b/config-ui/src/plugins/register/jira/data-scope.tsx
index 0d5a45f07..15f4e3800 100644
--- a/config-ui/src/plugins/register/jira/data-scope.tsx
+++ b/config-ui/src/plugins/register/jira/data-scope.tsx
@@ -16,24 +16,28 @@
*
*/
-import React from 'react';
-
import type { ScopeItemType } from './types';
import { MillerColumns } from './components/miller-columns';
interface Props {
connectionId: ID;
+ disabledItems?: ScopeItemType[];
selectedItems: ScopeItemType[];
onChangeItems: (selectedItems: ScopeItemType[]) => void;
}
-export const JiraDataScope = ({ connectionId, selectedItems, onChangeItems }: Props) => {
+export const JiraDataScope = ({ connectionId, disabledItems, selectedItems, onChangeItems }: Props) => {
return (
<>
<h3>Boards *</h3>
<p>Select the boards you would like to sync.</p>
- <MillerColumns connectionId={connectionId} selectedItems={selectedItems} onChangeItems={onChangeItems} />
+ <MillerColumns
+ connectionId={connectionId}
+ disabledItems={disabledItems}
+ selectedItems={selectedItems}
+ onChangeItems={onChangeItems}
+ />
</>
);
};
diff --git a/config-ui/src/plugins/register/pagerduty/data-scope.tsx b/config-ui/src/plugins/register/pagerduty/data-scope.tsx
index 59c4e9d73..1bd6d2f11 100644
--- a/config-ui/src/plugins/register/pagerduty/data-scope.tsx
+++ b/config-ui/src/plugins/register/pagerduty/data-scope.tsx
@@ -25,6 +25,7 @@ import * as S from './styled';
interface Props {
connectionId: ID;
+ disabledItems?: ScopeItemType[];
selectedItems: ScopeItemType[];
onChangeItems: (selectedItems: ScopeItemType[]) => void;
}
@@ -35,6 +36,11 @@ export const PagerDutyDataScope = ({ connectionId, onChangeItems, ...props }: Pr
[props.selectedItems],
);
+ const disabledItems = useMemo(
+ () => (props.disabledItems ?? []).map((it) => ({ id: `${it.id}`, name: it.name, data: it })),
+ [props.disabledItems],
+ );
+
return (
<S.DataScope>
<h3>PagerDuty Services *</h3>
@@ -44,6 +50,7 @@ export const PagerDutyDataScope = ({ connectionId, onChangeItems, ...props }: Pr
columnCount={1}
plugin="pagerduty"
connectionId={connectionId}
+ disabledItems={disabledItems}
selectedItems={selectedItems}
onChangeItems={onChangeItems}
/>
diff --git a/config-ui/src/plugins/register/sonarqube/data-scope.tsx b/config-ui/src/plugins/register/sonarqube/data-scope.tsx
index 3653f98ed..3d22360af 100644
--- a/config-ui/src/plugins/register/sonarqube/data-scope.tsx
+++ b/config-ui/src/plugins/register/sonarqube/data-scope.tsx
@@ -16,7 +16,7 @@
*
*/
-import React, { useMemo } from 'react';
+import { useMemo } from 'react';
import { DataScopeMillerColumns } from '@/plugins';
@@ -24,6 +24,7 @@ import type { SonarQubeScopeType } from './types';
interface Props {
connectionId: ID;
+ disabledItems?: SonarQubeScopeType[];
selectedItems: SonarQubeScopeType[];
onChangeItems: (selectedItems: SonarQubeScopeType[]) => void;
}
@@ -34,6 +35,11 @@ export const SonarQubeDataScope = ({ connectionId, onChangeItems, ...props }: Pr
[props.selectedItems],
);
+ const disabledItems = useMemo(
+ () => (props.disabledItems ?? []).map((it) => ({ id: it.projectKey, data: it })),
+ [props.disabledItems],
+ );
+
return (
<>
<h4>Add Repositories by Selecting from the Directory</h4>
@@ -43,6 +49,7 @@ export const SonarQubeDataScope = ({ connectionId, onChangeItems, ...props }: Pr
title="Projects"
plugin="sonarqube"
connectionId={connectionId}
+ disabledItems={disabledItems}
selectedItems={selectedItems}
onChangeItems={onChangeItems}
/>
diff --git a/config-ui/src/plugins/register/tapd/data-scope.tsx b/config-ui/src/plugins/register/tapd/data-scope.tsx
index 345cd73c2..4362b2f1d 100644
--- a/config-ui/src/plugins/register/tapd/data-scope.tsx
+++ b/config-ui/src/plugins/register/tapd/data-scope.tsx
@@ -16,7 +16,7 @@
*
*/
-import React, { useMemo, useState } from 'react';
+import { useMemo, useState } from 'react';
import { DataScopeMillerColumns } from '@/plugins';
@@ -27,6 +27,7 @@ import { ExternalLink } from '@/components';
interface Props {
connectionId: ID;
+ disabledItems?: ScopeItemType[];
selectedItems: ScopeItemType[];
onChangeItems: (selectedItems: ScopeItemType[]) => void;
}
@@ -37,6 +38,11 @@ export const TapdDataScope = ({ connectionId, onChangeItems, ...props }: Props)
[props.selectedItems],
);
+ const disabledItems = useMemo(
+ () => (props.disabledItems ?? []).map((it) => ({ id: `${it.id}`, name: it.name, data: it })),
+ [props.disabledItems],
+ );
+
const [pageToken, setPageToken] = useState<string | undefined>(undefined);
const [companyId, setCompanyId] = useState<string>(
localStorage.getItem(`plugin/tapd/connections/${connectionId}/company_id`) || '',
@@ -81,6 +87,7 @@ export const TapdDataScope = ({ connectionId, onChangeItems, ...props }: Props)
key={pageToken}
plugin="tapd"
connectionId={connectionId}
+ disabledItems={disabledItems}
selectedItems={selectedItems}
onChangeItems={onChangeItems}
pageToken={pageToken}
diff --git a/config-ui/src/plugins/register/zentao/data-scope.tsx b/config-ui/src/plugins/register/zentao/data-scope.tsx
index 601499135..ab2f60ea4 100644
--- a/config-ui/src/plugins/register/zentao/data-scope.tsx
+++ b/config-ui/src/plugins/register/zentao/data-scope.tsx
@@ -16,7 +16,7 @@
*
*/
-import React, { useMemo } from 'react';
+import { useMemo } from 'react';
import { DataScopeMillerColumns } from '@/plugins';
@@ -24,6 +24,7 @@ import type { ScopeItemType } from './types';
interface Props {
connectionId: ID;
+ disabledItems?: ScopeItemType[];
selectedItems: ScopeItemType[];
onChangeItems: (selectedItems: ScopeItemType[]) => void;
}
@@ -39,6 +40,16 @@ export const ZentaoDataScope = ({ connectionId, onChangeItems, ...props }: Props
[props.selectedItems],
);
+ const disabledItems = useMemo(
+ () =>
+ (props.disabledItems ?? []).map((it) => ({
+ id: it.type === 'project' ? `project/${it.id}` : `product/${it.id}`,
+ name: it.name,
+ data: it,
+ })),
+ [props.disabledItems],
+ );
+
return (
<>
<h3>Repositories *</h3>
@@ -46,6 +57,7 @@ export const ZentaoDataScope = ({ connectionId, onChangeItems, ...props }: Props
<DataScopeMillerColumns
plugin="zentao"
connectionId={connectionId}
+ disabledItems={disabledItems}
selectedItems={selectedItems}
onChangeItems={onChangeItems}
/>
diff --git a/config-ui/src/store/index.ts b/config-ui/src/store/index.ts
index 4e6e8a4de..a247f9b21 100644
--- a/config-ui/src/store/index.ts
+++ b/config-ui/src/store/index.ts
@@ -17,3 +17,4 @@
*/
export * from './connections';
+export * from './tips';
diff --git a/config-ui/src/plugins/register/jira/data-scope.tsx b/config-ui/src/store/tips/context.tsx
similarity index 57%
copy from config-ui/src/plugins/register/jira/data-scope.tsx
copy to config-ui/src/store/tips/context.tsx
index 0d5a45f07..51e9ffef1 100644
--- a/config-ui/src/plugins/register/jira/data-scope.tsx
+++ b/config-ui/src/store/tips/context.tsx
@@ -16,24 +16,31 @@
*
*/
-import React from 'react';
+import React, { useState, useContext } from 'react';
-import type { ScopeItemType } from './types';
+const TipsContext = React.createContext<{
+ text: React.ReactNode;
+ setText: React.Dispatch<React.SetStateAction<React.ReactNode>>;
+}>({
+ text: '',
+ setText: () => {},
+});
-import { MillerColumns } from './components/miller-columns';
+export const TipsContextProvider = ({ children }: { children: React.ReactNode }) => {
+ const [text, setText] = useState<React.ReactNode>();
-interface Props {
- connectionId: ID;
- selectedItems: ScopeItemType[];
- onChangeItems: (selectedItems: ScopeItemType[]) => void;
-}
-
-export const JiraDataScope = ({ connectionId, selectedItems, onChangeItems }: Props) => {
return (
- <>
- <h3>Boards *</h3>
- <p>Select the boards you would like to sync.</p>
- <MillerColumns connectionId={connectionId} selectedItems={selectedItems} onChangeItems={onChangeItems} />
- </>
+ <TipsContext.Provider
+ value={{
+ text,
+ setText,
+ }}
+ >
+ {children}
+ </TipsContext.Provider>
);
};
+
+export const TipsContextConsumer = TipsContext.Consumer;
+
+export const useTips = () => useContext(TipsContext);
diff --git a/config-ui/src/store/index.ts b/config-ui/src/store/tips/index.ts
similarity index 96%
copy from config-ui/src/store/index.ts
copy to config-ui/src/store/tips/index.ts
index 4e6e8a4de..5cdc9167c 100644
--- a/config-ui/src/store/index.ts
+++ b/config-ui/src/store/tips/index.ts
@@ -16,4 +16,4 @@
*
*/
-export * from './connections';
+export * from './context';