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/03/28 03:11:31 UTC

[incubator-devlake] branch main updated: feat(config-ui): support teambition connection config (#4777)

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 0b86170b5 feat(config-ui): support teambition connection config (#4777)
0b86170b5 is described below

commit 0b86170b5d1ff15727a64e4fbb1b9cc23400e085
Author: coldgust <11...@users.noreply.github.com>
AuthorDate: Tue Mar 28 11:11:25 2023 +0800

    feat(config-ui): support teambition connection config (#4777)
    
    * feat(config-ui): support teambition connection config
    
    * feat(config-ui): support teambition connection config
---
 .../components/connection-form/fields/app-id.tsx   | 83 ++++++++++++++++++++
 .../components/connection-form/fields/index.tsx    |  6 ++
 .../connection-form/fields/secret-key.tsx          | 83 ++++++++++++++++++++
 .../components/connection-form/operate/test.tsx    |  3 +-
 config-ui/src/plugins/config.ts                    |  2 +
 .../plugins/register/teambition/assets/icon.svg    |  5 ++
 .../src/plugins/register/teambition/config.tsx     | 90 ++++++++++++++++++++++
 .../register/teambition/connection-fields/index.ts | 20 +++++
 .../teambition/connection-fields/styled.ts         | 32 ++++++++
 .../teambition/connection-fields/tenant-id.tsx     | 77 ++++++++++++++++++
 .../teambition/connection-fields/tenant-type.tsx   | 77 ++++++++++++++++++
 config-ui/src/plugins/register/teambition/index.ts | 19 +++++
 12 files changed, 496 insertions(+), 1 deletion(-)

diff --git a/config-ui/src/plugins/components/connection-form/fields/app-id.tsx b/config-ui/src/plugins/components/connection-form/fields/app-id.tsx
new file mode 100644
index 000000000..5041e509b
--- /dev/null
+++ b/config-ui/src/plugins/components/connection-form/fields/app-id.tsx
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ *
+ */
+/*
+ * 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, { useEffect } from 'react';
+import { FormGroup, InputGroup } from '@blueprintjs/core';
+
+import * as S from './styled';
+
+interface Props {
+  label?: string;
+  subLabel?: string;
+  placeholder?: string;
+  name: string;
+  initialValue: string;
+  value: string;
+  error: string;
+  setValue: (value: string) => void;
+  setError: (error: string) => void;
+}
+
+export const ConnectionAppId = ({
+                                     label,
+                                     subLabel,
+                                     placeholder,
+                                     initialValue,
+                                     value,
+                                     setValue,
+                                     setError,
+                                   }: Props) => {
+  useEffect(() => {
+    setValue(initialValue);
+  }, [initialValue]);
+
+  useEffect(() => {
+    setError(value ? '' : 'appid is required');
+  }, [value]);
+
+  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+    setValue(e.target.value);
+  };
+
+  return (
+    <FormGroup
+      label={<S.Label>{label ?? 'AppId'}</S.Label>}
+      labelInfo={<S.LabelInfo>*</S.LabelInfo>}
+      subLabel={subLabel ? <S.LabelDescription>{subLabel}</S.LabelDescription> : null}
+    >
+      <InputGroup placeholder={placeholder ?? 'Your AppId'} value={value} onChange={handleChange} />
+    </FormGroup>
+  );
+};
diff --git a/config-ui/src/plugins/components/connection-form/fields/index.tsx b/config-ui/src/plugins/components/connection-form/fields/index.tsx
index 74027e33f..0b079d41e 100644
--- a/config-ui/src/plugins/components/connection-form/fields/index.tsx
+++ b/config-ui/src/plugins/components/connection-form/fields/index.tsx
@@ -25,6 +25,8 @@ import { ConnectionPassword } from './password';
 import { ConnectionToken } from './token';
 import { ConnectionProxy } from './proxy';
 import { ConnectionRateLimit } from './rate-limit';
+import { ConnectionAppId } from "./app-id";
+import { ConnectionSecretKey } from "./secret-key";
 
 interface Props {
   name: string;
@@ -76,6 +78,10 @@ export const Form = ({ name, fields, initialValues, values, errors, setValues, s
           return <ConnectionPassword key={key} {...getProps('password')} {...field} />;
         case 'token':
           return <ConnectionToken key={key} {...getProps('token')} {...field} />;
+        case 'appId':
+          return <ConnectionAppId key={key} {...getProps('appId')} {...field} />;
+        case 'secretKey':
+          return <ConnectionSecretKey key={key} {...getProps('secretKey')} {...field} />;
         case 'proxy':
           return <ConnectionProxy key={key} {...getProps('proxy')} {...field} />;
         case 'rateLimitPerHour':
diff --git a/config-ui/src/plugins/components/connection-form/fields/secret-key.tsx b/config-ui/src/plugins/components/connection-form/fields/secret-key.tsx
new file mode 100644
index 000000000..88cc42ba6
--- /dev/null
+++ b/config-ui/src/plugins/components/connection-form/fields/secret-key.tsx
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ *
+ */
+/*
+ * 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, { useEffect } from 'react';
+import { FormGroup, InputGroup } from '@blueprintjs/core';
+
+import * as S from './styled';
+
+interface Props {
+  label?: string;
+  subLabel?: string;
+  placeholder?: string;
+  name: string;
+  initialValue: string;
+  value: string;
+  error: string;
+  setValue: (value: string) => void;
+  setError: (value: string) => void;
+}
+
+export const ConnectionSecretKey = ({
+  label,
+  subLabel,
+  placeholder,
+  initialValue,
+  value,
+  setValue,
+  setError,
+}: Props) => {
+  useEffect(() => {
+    setValue(initialValue);
+  }, [initialValue]);
+
+  useEffect(() => {
+    setError(value ? '' : 'SecretKey is required');
+  }, [value]);
+
+  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+    setValue(e.target.value);
+  };
+
+  return (
+    <FormGroup
+      label={<S.Label>{label ?? 'SecretKey'}</S.Label>}
+      labelInfo={<S.LabelInfo>*</S.LabelInfo>}
+      subLabel={subLabel ? <S.LabelDescription>{subLabel}</S.LabelDescription> : null}
+    >
+      <InputGroup type="password" placeholder={placeholder ?? 'Your SecretKey'} value={value} onChange={handleChange} />
+    </FormGroup>
+  );
+};
diff --git a/config-ui/src/plugins/components/connection-form/operate/test.tsx b/config-ui/src/plugins/components/connection-form/operate/test.tsx
index 3e49831e0..a8446e160 100644
--- a/config-ui/src/plugins/components/connection-form/operate/test.tsx
+++ b/config-ui/src/plugins/components/connection-form/operate/test.tsx
@@ -39,7 +39,8 @@ export const Test = ({ plugin, values, errors }: Props) => {
 
   const handleSubmit = () => {
     onSubmit(
-      pick(values, ['endpoint', 'token', 'username', 'password', 'app_id', 'secret_key', 'proxy', 'authMethod']),
+      pick(values, ['endpoint', 'token', 'username', 'password', 'proxy', 'authMethod',
+        'appId', 'secretKey', 'tenantId', 'tenantType']),
     );
   };
 
diff --git a/config-ui/src/plugins/config.ts b/config-ui/src/plugins/config.ts
index b4d836e05..ccb270c08 100644
--- a/config-ui/src/plugins/config.ts
+++ b/config-ui/src/plugins/config.ts
@@ -37,6 +37,7 @@ 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";
 
 export const PluginConfig: PluginConfigType[] = [
   AEConfig,
@@ -57,6 +58,7 @@ export const PluginConfig: PluginConfigType[] = [
   SonarQubeConfig,
   StarRocksConfig,
   TAPDConfig,
+  TeambitionConfig,
   ZenTaoConfig,
   WebhookConfig,
 ].sort((a, b) => a.sort - b.sort);
diff --git a/config-ui/src/plugins/register/teambition/assets/icon.svg b/config-ui/src/plugins/register/teambition/assets/icon.svg
new file mode 100644
index 000000000..93d227f72
--- /dev/null
+++ b/config-ui/src/plugins/register/teambition/assets/icon.svg
@@ -0,0 +1,5 @@
+<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M24.8601 7.75227C24.8606 6.58118 25.3105 5.45494 26.1169 4.60578C26.9234 3.75663 28.025 3.24932 29.1945 3.18845C26.7937 3.06965 24.1226 3 21.345 3C10.6606 3 2 3.98733 2 5.20407C2 6.42082 10.6606 7.40814 21.345 7.40814V12.3243H28.9487C28.7076 12.3059 28.4704 12.2535 28.244 12.1686C27.2724 11.9079 26.414 11.3336 25.8021 10.5351C25.1902 9.73651 24.8591 8.7583 24.8601 7.75227Z" fill="#BDCEFB"/>
+<path d="M21.345 7.40329C10.6606 7.40329 2 6.41596 2 5.19922V13.1757C2 14.0892 6.86288 14.8717 13.7987 15.2241V30.9066L21.4024 32.3609L21.345 7.40329Z" fill="#7497F7"/>
+<path d="M29.428 12.3237C31.953 12.3237 34 10.2768 34 7.7517C34 5.22665 31.953 3.17969 29.428 3.17969C26.9029 3.17969 24.856 5.22665 24.856 7.7517C24.856 10.2768 26.9029 12.3237 29.428 12.3237Z" fill="#7497F7"/>
+</svg>
diff --git a/config-ui/src/plugins/register/teambition/config.tsx b/config-ui/src/plugins/register/teambition/config.tsx
new file mode 100644
index 000000000..757f6c9a0
--- /dev/null
+++ b/config-ui/src/plugins/register/teambition/config.tsx
@@ -0,0 +1,90 @@
+/*
+ * 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 from 'react';
+
+import type { PluginConfigType } from '@/plugins';
+import { PluginType } from '@/plugins';
+
+import Icon from './assets/icon.svg';
+import { ConnectionTenantId, ConnectionTenantType } from "./connection-fields";
+
+export const TeambitionConfig: PluginConfigType = {
+  type: PluginType.Connection,
+  plugin: 'teambition',
+  name: 'Teambition',
+  isBeta: true,
+  icon: Icon,
+  sort: 100,
+  connection: {
+    docLink: 'https://devlake.apache.org/docs/Configuration/Teambition',
+    initialValues: {
+      endpoint: 'https://open.teambition.com/api/',
+      tenantType: 'organization'
+    },
+    fields: [
+      'name',
+      {
+        key: 'endpoint',
+        subLabel: 'You do not need to enter the endpoint URL, because all versions use the same URL.',
+        disabled: true,
+      },
+      {
+        key: 'appId',
+        label: 'Application App Id',
+        subLabel: 'Your teambition application App Id.',
+      },
+      {
+        key: 'secretKey',
+        label: 'Application Secret Key',
+        subLabel: 'Your teambition application App Secret.',
+      },
+      ({ initialValues, values, errors, setValues, setErrors }: any) => (
+        <ConnectionTenantId
+          key="tenantId"
+          name="tenantId"
+          value={values.tenantId ?? ''}
+          error={errors.tenantId ?? ''}
+          setValue={(value) => setValues({ tenantId: value })}
+          setError={(value) => setErrors({ tenantId: value })}
+         initialValue={initialValues.tenantId} />
+      ),
+      ({ initialValues, values, errors, setValues, setErrors }: any) => (
+        <ConnectionTenantType
+          key="tenantType"
+          name="tenantType"
+          value={values.tenantType ?? ''}
+          error={errors.tenantType ?? ''}
+          setValue={(value) => setValues({ tenantType: value })}
+          setError={(value) => setErrors({ tenantType: value })}
+          initialValue={initialValues.tenantType} />
+      ),
+      'proxy',
+      {
+        key: 'rateLimitPerHour',
+        subLabel:
+          'By default, DevLake uses 3,000 requests/hour for data collection for TAPD. But you can adjust the collection speed by setting up your desirable rate limit.',
+        learnMore: 'https://devlake.apache.org/docs/Configuration/Tapd#fixed-rate-limit-optional',
+        externalInfo: 'The maximum rate limit of TAPD is 3,600 requests/hour.',
+        defaultValue: 3000,
+      },
+    ],
+  },
+  entities: ['TICKET'],
+  transformation: {},
+};
diff --git a/config-ui/src/plugins/register/teambition/connection-fields/index.ts b/config-ui/src/plugins/register/teambition/connection-fields/index.ts
new file mode 100644
index 000000000..b2481eb94
--- /dev/null
+++ b/config-ui/src/plugins/register/teambition/connection-fields/index.ts
@@ -0,0 +1,20 @@
+/*
+ * 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 './tenant-id';
+export * from './tenant-type';
diff --git a/config-ui/src/plugins/register/teambition/connection-fields/styled.ts b/config-ui/src/plugins/register/teambition/connection-fields/styled.ts
new file mode 100644
index 000000000..82ec96a20
--- /dev/null
+++ b/config-ui/src/plugins/register/teambition/connection-fields/styled.ts
@@ -0,0 +1,32 @@
+/*
+ * 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 styled from 'styled-components';
+
+export const Label = styled.label`
+  font-size: 16px;
+  font-weight: 600;
+`;
+
+export const LabelInfo = styled.i`
+  color: #ff8b8b;
+`;
+
+export const LabelDescription = styled.p`
+  margin: 0;
+`;
\ No newline at end of file
diff --git a/config-ui/src/plugins/register/teambition/connection-fields/tenant-id.tsx b/config-ui/src/plugins/register/teambition/connection-fields/tenant-id.tsx
new file mode 100644
index 000000000..cea5c8e35
--- /dev/null
+++ b/config-ui/src/plugins/register/teambition/connection-fields/tenant-id.tsx
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ *
+ */
+/*
+ * 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, { useEffect } from 'react';
+import { FormGroup, InputGroup } from '@blueprintjs/core';
+
+import * as S from './styled';
+
+interface Props {
+  name: string;
+  initialValue: string;
+  value: string;
+  error: string;
+  setValue: (value: string) => void;
+  setError: (error: string) => void;
+}
+
+export const ConnectionTenantId = ({
+                                     initialValue,
+                                     value,
+                                     setValue,
+                                     setError,
+                                   }: Props) => {
+  useEffect(() => {
+    setValue(initialValue);
+  }, [initialValue]);
+
+  useEffect(() => {
+    setError(value ? '' : 'TenantId is required');
+  }, [value]);
+
+  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+    setValue(e.target.value);
+  };
+
+  return (
+    <FormGroup
+      label={<S.Label>Tenant Id</S.Label>}
+      labelInfo={<S.LabelInfo>*</S.LabelInfo>}
+      subLabel={<S.LabelDescription>Your teambition organization id.</S.LabelDescription>}
+    >
+      <InputGroup placeholder="Your TenantId" value={value} onChange={handleChange} />
+    </FormGroup>
+  );
+};
diff --git a/config-ui/src/plugins/register/teambition/connection-fields/tenant-type.tsx b/config-ui/src/plugins/register/teambition/connection-fields/tenant-type.tsx
new file mode 100644
index 000000000..c4ce96d8f
--- /dev/null
+++ b/config-ui/src/plugins/register/teambition/connection-fields/tenant-type.tsx
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ *
+ */
+/*
+ * 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, { useEffect } from 'react';
+import { FormGroup, InputGroup } from '@blueprintjs/core';
+
+import * as S from './styled';
+
+interface Props {
+  name: string;
+  initialValue: string;
+  value: string;
+  error: string;
+  setValue: (value: string) => void;
+  setError: (error: string) => void;
+}
+
+export const ConnectionTenantType = ({
+                                     initialValue,
+                                     value,
+                                     setValue,
+                                     setError,
+                                   }: Props) => {
+  useEffect(() => {
+    setValue(initialValue);
+  }, [initialValue]);
+
+  useEffect(() => {
+    setError(value ? '' : 'TenantType is required');
+  }, [value]);
+
+  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+    setValue(e.target.value);
+  };
+
+  return (
+    <FormGroup
+      label={<S.Label>Tenant Type</S.Label>}
+      labelInfo={<S.LabelInfo>*</S.LabelInfo>}
+      subLabel={<S.LabelDescription>You do not need to enter the tenant type, because teambition only supports 'organization' type currently.</S.LabelDescription>}
+    >
+      <InputGroup disabled={true} placeholder="Your API Tenant Type" value={value} onChange={handleChange} />
+    </FormGroup>
+  );
+};
diff --git a/config-ui/src/plugins/register/teambition/index.ts b/config-ui/src/plugins/register/teambition/index.ts
new file mode 100644
index 000000000..de415db39
--- /dev/null
+++ b/config-ui/src/plugins/register/teambition/index.ts
@@ -0,0 +1,19 @@
+/*
+ * 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 './config';