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/01/18 02:11:11 UTC

[incubator-devlake] branch main updated: feat(config-ui): simplify and re-organize transformation configuration for github (#4219)

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 35e022205 feat(config-ui): simplify and re-organize transformation configuration for github (#4219)
35e022205 is described below

commit 35e0222055139d2414d82d2c92f9cde6716dee24
Author: 青湛 <0x...@gmail.com>
AuthorDate: Wed Jan 18 10:11:05 2023 +0800

    feat(config-ui): simplify and re-organize transformation configuration for github (#4219)
    
    * refactor(config-ui): move components/text-tooltip to components/tooltip/text-tooltip
    
    * feat(config-ui): add new component icon-tooltip
    
    * feat(config-ui): add new component external-link
    
    * refactor(config-ui): rename icon-tooltip to help-tooltip
    
    * feat(config-ui): add global text color
    
    * refactor(config-ui): adjust the global style
    
    * refactor(config-ui): adjust github transformation
    
    * feat(config-ui): add tips for plugin transformation
    
    * refactor(config-ui): adjust the style for create blueprint
---
 .../action/external-link/index.tsx}                |  21 +-
 config-ui/src/components/action/index.ts           |   1 +
 config-ui/src/components/index.ts                  |   2 +-
 .../help-tooltip}/index.tsx                        |  31 +-
 .../styled.ts => components/tooltip/index.ts}      |  12 +-
 .../{ => tooltip}/text-tooltip/index.tsx           |   0
 config-ui/src/index.css                            |  16 +-
 config-ui/src/pages/blueprint/create/styled.ts     |  15 -
 .../plugins/components/transformation/index.tsx    |  10 +-
 .../plugins/components/transformation/styled.ts    |  20 +-
 .../components/additional-settings/index.tsx       |  94 -----
 .../register/github/components/ci-cd/index.tsx     |  96 -----
 .../github/components/code-review/index.tsx        | 107 ------
 .../plugins/register/github/components/index.ts    |   4 -
 .../github/components/issue-tracking/index.tsx     | 128 -------
 config-ui/src/plugins/register/github/styled.ts    |  57 ++-
 .../src/plugins/register/github/transformation.tsx | 396 ++++++++++++++++++++-
 17 files changed, 502 insertions(+), 508 deletions(-)

diff --git a/config-ui/src/plugins/register/github/components/code-review/styled.ts b/config-ui/src/components/action/external-link/index.tsx
similarity index 72%
copy from config-ui/src/plugins/register/github/components/code-review/styled.ts
copy to config-ui/src/components/action/external-link/index.tsx
index 6b79162dc..6888c56f0 100644
--- a/config-ui/src/plugins/register/github/components/code-review/styled.ts
+++ b/config-ui/src/components/action/external-link/index.tsx
@@ -16,13 +16,18 @@
  *
  */
 
-import styled from 'styled-components';
+import React from 'react';
 
-export const Tips = styled.div`
-  display: flex;
-  align-items: center;
-`;
+interface Props {
+  link: string;
+  children: React.ReactNode;
+  style?: React.CSSProperties;
+}
 
-export const TipsContent = styled.div`
-  padding: 8px;
-`;
+export const ExternalLink = ({ link, children, style }: Props) => {
+  return (
+    <a href={link} rel="noreferrer" target="_blank" style={style}>
+      {children}
+    </a>
+  );
+};
diff --git a/config-ui/src/components/action/index.ts b/config-ui/src/components/action/index.ts
index 06dc0b92e..068e974c7 100644
--- a/config-ui/src/components/action/index.ts
+++ b/config-ui/src/components/action/index.ts
@@ -17,4 +17,5 @@
  */
 
 export * from './delete-button';
+export * from './external-link';
 export * from './icon-button';
diff --git a/config-ui/src/components/index.ts b/config-ui/src/components/index.ts
index d46332c87..cc8256d2c 100644
--- a/config-ui/src/components/index.ts
+++ b/config-ui/src/components/index.ts
@@ -27,4 +27,4 @@ export * from './logo';
 export * from './card';
 export * from './inspector';
 export * from './action';
-export * from './text-tooltip';
+export * from './tooltip';
diff --git a/config-ui/src/components/text-tooltip/index.tsx b/config-ui/src/components/tooltip/help-tooltip/index.tsx
similarity index 66%
copy from config-ui/src/components/text-tooltip/index.tsx
copy to config-ui/src/components/tooltip/help-tooltip/index.tsx
index 03d90e766..905a24af6 100644
--- a/config-ui/src/components/text-tooltip/index.tsx
+++ b/config-ui/src/components/tooltip/help-tooltip/index.tsx
@@ -17,32 +17,33 @@
  */
 
 import React from 'react';
-import type { IntentProps } from '@blueprintjs/core';
-import { Position } from '@blueprintjs/core';
+import { Icon, Position } from '@blueprintjs/core';
 import { Tooltip2 } from '@blueprintjs/popover2';
 import styled from 'styled-components';
 
-const Wrapper = styled.div`
-  width: 100%;
+const Wrapper = styled.span`
+  margin-left: 4px;
 
   & > .bp4-popover2-target {
-    display: block;
-    overflow: hidden;
-    white-space: nowrap;
-    text-overflow: ellipsis;
+    display: inline-block !important;
+
+    .bp4-icon {
+      display: block;
+      cursor: pointer;
+    }
   }
 `;
 
-interface Props extends IntentProps {
-  content: string;
-  children: React.ReactNode;
+interface Props {
+  content: string | JSX.Element;
+  style?: React.CSSProperties;
 }
 
-export const TextTooltip = ({ intent, content, children }: Props) => {
+export const HelpTooltip = ({ content, style }: Props) => {
   return (
-    <Wrapper>
-      <Tooltip2 intent={intent} position={Position.TOP} content={content}>
-        {children}
+    <Wrapper style={style}>
+      <Tooltip2 position={Position.TOP} content={content}>
+        <Icon icon="help" size={12} color="#94959f" />
       </Tooltip2>
     </Wrapper>
   );
diff --git a/config-ui/src/plugins/register/github/components/code-review/styled.ts b/config-ui/src/components/tooltip/index.ts
similarity index 82%
rename from config-ui/src/plugins/register/github/components/code-review/styled.ts
rename to config-ui/src/components/tooltip/index.ts
index 6b79162dc..e2d120c3a 100644
--- a/config-ui/src/plugins/register/github/components/code-review/styled.ts
+++ b/config-ui/src/components/tooltip/index.ts
@@ -16,13 +16,5 @@
  *
  */
 
-import styled from 'styled-components';
-
-export const Tips = styled.div`
-  display: flex;
-  align-items: center;
-`;
-
-export const TipsContent = styled.div`
-  padding: 8px;
-`;
+export * from './help-tooltip';
+export * from './text-tooltip';
diff --git a/config-ui/src/components/text-tooltip/index.tsx b/config-ui/src/components/tooltip/text-tooltip/index.tsx
similarity index 100%
rename from config-ui/src/components/text-tooltip/index.tsx
rename to config-ui/src/components/tooltip/text-tooltip/index.tsx
diff --git a/config-ui/src/index.css b/config-ui/src/index.css
index 352b633b2..5a47c905e 100644
--- a/config-ui/src/index.css
+++ b/config-ui/src/index.css
@@ -23,6 +23,20 @@
 @import '~@blueprintjs/popover2/lib/css/blueprint-popover2.css';
 @import '~@blueprintjs/datetime/lib/css/blueprint-datetime.css';
 
+body {
+  color: #292b3f;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+  margin: 0;
+  font-weight: 600;
+}
+
 h1 {
   font-size: 20px;
 }
@@ -54,7 +68,7 @@ ul {
 }
 
 p {
-  margin: 0 0 8px 0;
+  margin: 8px 0 16px;
   font-size: 12px;
   color: #94959f;
 }
diff --git a/config-ui/src/pages/blueprint/create/styled.ts b/config-ui/src/pages/blueprint/create/styled.ts
index 4084d01c6..378109925 100644
--- a/config-ui/src/pages/blueprint/create/styled.ts
+++ b/config-ui/src/pages/blueprint/create/styled.ts
@@ -23,26 +23,11 @@ export const Container = styled.div``;
 export const Content = styled.div`
   margin-top: 36px;
   margin-bottom: 24px;
-  font-size: 12px;
 
   .card + .card {
     margin-top: 24px;
   }
 
-  h2 {
-    margin: 0;
-    padding: 0;
-    font-size: 16px;
-    font-weight: 600;
-  }
-
-  h3 {
-    margin: 0 0 8px;
-    padding: 0;
-    font-size: 14px;
-    font-weight: 600;
-  }
-
   .back {
     display: flex;
     align-items: center;
diff --git a/config-ui/src/plugins/components/transformation/index.tsx b/config-ui/src/plugins/components/transformation/index.tsx
index cb2c04a7f..7ff8e4ce3 100644
--- a/config-ui/src/plugins/components/transformation/index.tsx
+++ b/config-ui/src/plugins/components/transformation/index.tsx
@@ -19,7 +19,7 @@
 import React, { useState } from 'react';
 import { RadioGroup, Radio, InputGroup, ButtonGroup, Button, Intent } from '@blueprintjs/core';
 
-import { Divider, Selector, MultiSelector } from '@/components';
+import { ExternalLink, Divider, Selector, MultiSelector } from '@/components';
 
 import { GitHubTransformation } from '@/plugins/register/github';
 import { JIRATransformation } from '@/plugins/register/jira';
@@ -59,6 +59,14 @@ export const Transformation = ({ from, plugin, connectionId, onCancel, ...props
 
   return (
     <S.Wrapper>
+      <div className="tips">
+        To learn about how GitHub transformation is used in DevLake,
+        <ExternalLink link="https://devlake.apache.org/docs/UserManuals/ConfigUI/GitHub#step-3---adding-transformation-rules-optional">
+          check out this doc
+        </ExternalLink>
+        .
+      </div>
+
       <div className="block">
         <RadioGroup selectedValue={type} onChange={handleChangeType}>
           <Radio label="Creating a new transformation" value="create" />
diff --git a/config-ui/src/plugins/components/transformation/styled.ts b/config-ui/src/plugins/components/transformation/styled.ts
index 2c42d11e7..dc39bf265 100644
--- a/config-ui/src/plugins/components/transformation/styled.ts
+++ b/config-ui/src/plugins/components/transformation/styled.ts
@@ -19,6 +19,14 @@
 import styled from 'styled-components';
 
 export const Wrapper = styled.div`
+  .tips {
+    margin-bottom: 16px;
+    padding: 12px 24px;
+    background: #f0f4fe;
+    border: 1px solid #bdcefb;
+    border-radius: 4px;
+  }
+
   .block {
     padding: 8px 16px;
   }
@@ -27,18 +35,6 @@ export const Wrapper = styled.div`
     margin-top: 16px;
   }
 
-  h3 {
-    margin: 0 0 8px;
-
-    .bp4-tag {
-      margin-left: 6px;
-    }
-  }
-
-  p {
-    margin: 0 0 8px;
-  }
-
   .bp4-form-group {
     display: flex;
     align-items: center;
diff --git a/config-ui/src/plugins/register/github/components/additional-settings/index.tsx b/config-ui/src/plugins/register/github/components/additional-settings/index.tsx
deleted file mode 100644
index abcd22d5d..000000000
--- a/config-ui/src/plugins/register/github/components/additional-settings/index.tsx
+++ /dev/null
@@ -1,94 +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 } from 'react';
-import { Checkbox, FormGroup, InputGroup, NumericInput } from '@blueprintjs/core';
-
-interface Props {
-  transformation: any;
-  setTransformation: React.Dispatch<React.SetStateAction<any>>;
-}
-
-export const AdditionalSettings = ({ transformation, setTransformation }: Props) => {
-  const [enable, setEnable] = useState(false);
-
-  const handleToggleEnable = () => {
-    setEnable(!enable);
-  };
-
-  return (
-    <>
-      <h3>Additional Settings</h3>
-      <Checkbox
-        checked={enable}
-        label="Enable calculation of commit and issue difference"
-        onChange={handleToggleEnable}
-      />
-      {enable && (
-        <div className="additional-settings-refdiff">
-          <FormGroup inline label="Tags Limit">
-            <NumericInput
-              placeholder="10"
-              allowNumericCharactersOnly={true}
-              value={transformation.refdiff?.tagsLimit}
-              onValueChange={(tagsLimit) =>
-                setTransformation({
-                  ...transformation,
-                  refdiff: {
-                    ...transformation?.refdiff,
-                    tagsLimit,
-                  },
-                })
-              }
-            />
-          </FormGroup>
-          <FormGroup inline label="Tags Pattern">
-            <InputGroup
-              placeholder="(regex)$"
-              value={transformation.refdiff?.tagsPattern}
-              onChange={(e) =>
-                setTransformation({
-                  ...transformation,
-                  refdiff: {
-                    ...transformation?.refdiff,
-                    tagsPattern: e.target.value,
-                  },
-                })
-              }
-            />
-          </FormGroup>
-          <FormGroup inline label="Tags Order">
-            <InputGroup
-              placeholder="reverse semver"
-              value={transformation.refdiff?.tagsOrder}
-              onChange={(e) =>
-                setTransformation({
-                  ...transformation,
-                  refdiff: {
-                    ...transformation?.refdiff,
-                    tagsOrder: e.target.value,
-                  },
-                })
-              }
-            />
-          </FormGroup>
-        </div>
-      )}
-    </>
-  );
-};
diff --git a/config-ui/src/plugins/register/github/components/ci-cd/index.tsx b/config-ui/src/plugins/register/github/components/ci-cd/index.tsx
deleted file mode 100644
index 2a07d871f..000000000
--- a/config-ui/src/plugins/register/github/components/ci-cd/index.tsx
+++ /dev/null
@@ -1,96 +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 } from 'react';
-import { Tag, Intent, RadioGroup, Radio, FormGroup, InputGroup } from '@blueprintjs/core';
-
-interface Props {
-  transformation: any;
-  setTransformation: React.Dispatch<React.SetStateAction<any>>;
-}
-
-export const CiCd = ({ transformation, setTransformation }: Props) => {
-  const [enable, setEnable] = useState(1);
-
-  const handleChangeEnable = (e: number) => {
-    if (e === 0) {
-      setTransformation({
-        ...transformation,
-        deploymentPattern: undefined,
-        productionPattern: undefined,
-      });
-    } else {
-      setTransformation({
-        ...transformation,
-        deploymentPattern: '',
-        productionPattern: '',
-      });
-    }
-    setEnable(e);
-  };
-
-  return (
-    <>
-      <h3>CI/CD</h3>
-      <p>
-        <strong>What is a deployment?</strong>{' '}
-        <Tag minimal intent={Intent.PRIMARY} style={{ fontSize: '10px' }}>
-          DORA
-        </Tag>
-      </p>
-      <p>Define Deployment using one of the following options.</p>
-      <RadioGroup selectedValue={enable} onChange={(e) => handleChangeEnable(+(e.target as HTMLInputElement).value)}>
-        <Radio label="Detect Deployment from Jobs in GitHub Action" value={1} />
-        {enable === 1 && (
-          <div style={{ paddingLeft: 20 }}>
-            <p>A GitHub Action job with a name that matches the given regEx will be considered as a Deployment.</p>
-            <FormGroup inline label="Deployment">
-              <InputGroup
-                placeholder="(?i)deploy"
-                value={transformation.deploymentPattern}
-                onChange={(e) =>
-                  setTransformation({
-                    ...transformation,
-                    deploymentPattern: e.target.value,
-                  })
-                }
-              />
-            </FormGroup>
-            <p>
-              A GitHub Action job with a name that matches the given regEx will be considered as a job in the Production
-              environment. If you leave this field empty, all data will be tagged as in the Production environment.
-            </p>
-            <FormGroup inline label="Production">
-              <InputGroup
-                placeholder="(?i)production"
-                value={transformation.productionPattern}
-                onChange={(e) =>
-                  setTransformation({
-                    ...transformation,
-                    productionPattern: e.target.value,
-                  })
-                }
-              />
-            </FormGroup>
-          </div>
-        )}
-        <Radio label="Not using Jobs in GitHub Action as Deployments" value={0} />
-      </RadioGroup>
-    </>
-  );
-};
diff --git a/config-ui/src/plugins/register/github/components/code-review/index.tsx b/config-ui/src/plugins/register/github/components/code-review/index.tsx
deleted file mode 100644
index f34172708..000000000
--- a/config-ui/src/plugins/register/github/components/code-review/index.tsx
+++ /dev/null
@@ -1,107 +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 from 'react';
-import { Tag, FormGroup, InputGroup, TextArea, Icon, Colors, Position } from '@blueprintjs/core';
-import { Popover2 } from '@blueprintjs/popover2';
-
-import * as S from './styled';
-
-interface Props {
-  transformation: any;
-  setTransformation: React.Dispatch<React.SetStateAction<any>>;
-}
-
-export const CodeReview = ({ transformation, setTransformation }: Props) => {
-  return (
-    <>
-      <h3>
-        <span>Code Review</span>
-        <Tag minimal>RegExp</Tag>
-      </h3>
-      <p>Map your pull requests labels with each category to view corresponding metrics in the dashboard.</p>
-      <FormGroup inline label="Type">
-        <InputGroup
-          placeholder="type/(.*)$"
-          value={transformation.prType}
-          onChange={(e) => setTransformation({ ...transformation, prType: e.target.value })}
-        />
-      </FormGroup>
-      <FormGroup inline label="Component">
-        <InputGroup
-          placeholder="component/(.*)$"
-          value={transformation.prComponent}
-          onChange={(e) =>
-            setTransformation({
-              ...transformation,
-              prComponent: e.target.value,
-            })
-          }
-        />
-      </FormGroup>
-      <h3>
-        <span> PR-Issue Mapping</span>
-        <Tag minimal>RegExp</Tag>
-      </h3>
-      <p>
-        Extract the issue numbers closed by pull requests. The issue numbers are parsed from PR bodies that meet the
-        following RegEx.
-      </p>
-      <FormGroup
-        inline
-        label={
-          <S.Tips>
-            PR Body Pattern
-            <Popover2
-              position={Position.TOP}
-              content={
-                <S.TipsContent>
-                  <p>
-                    <Icon icon="tick-circle" size={10} color={Colors.GREEN4} style={{ marginRight: '4px' }} />
-                    Example 1: PR #321 body contains "<strong>Closes #1234</strong>" (PR #321 and issue #1234 will be
-                    mapped by the following RegEx)
-                  </p>
-                  <p>
-                    <Icon icon="delete" size={10} color={Colors.RED4} style={{ marginRight: '4px' }} />
-                    Example 2: PR #321 body contains "<strong>Related to #1234</strong>" (PR #321 and issue #1234 will
-                    NOT be mapped by the following RegEx)
-                  </p>
-                </S.TipsContent>
-              }
-            >
-              <Icon icon="help" size={12} color={Colors.GRAY3} style={{ marginLeft: '4px', marginBottom: '4px' }} />
-            </Popover2>
-          </S.Tips>
-        }
-      >
-        <TextArea
-          value={transformation.prBodyClosePattern}
-          placeholder="(?mi)(fix|close|resolve|fixes|closes|resolves|fixed|closed|resolved)[\s]*.*(((and )?(#|https:\/\/github.com\/%s\/issues\/)\d+[ ]*)+)"
-          onChange={(e) =>
-            setTransformation({
-              ...transformation,
-              prBodyClosePattern: e.target.value,
-            })
-          }
-          fill
-          rows={2}
-        />
-      </FormGroup>
-    </>
-  );
-};
diff --git a/config-ui/src/plugins/register/github/components/index.ts b/config-ui/src/plugins/register/github/components/index.ts
index 24ff97fb5..db6a4db91 100644
--- a/config-ui/src/plugins/register/github/components/index.ts
+++ b/config-ui/src/plugins/register/github/components/index.ts
@@ -18,7 +18,3 @@
 
 export * from './miller-columns';
 export * from './repo-selector';
-export * from './issue-tracking';
-export * from './ci-cd';
-export * from './code-review';
-export * from './additional-settings';
diff --git a/config-ui/src/plugins/register/github/components/issue-tracking/index.tsx b/config-ui/src/plugins/register/github/components/issue-tracking/index.tsx
deleted file mode 100644
index 0e7647043..000000000
--- a/config-ui/src/plugins/register/github/components/issue-tracking/index.tsx
+++ /dev/null
@@ -1,128 +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 from 'react';
-import { Tag, FormGroup, InputGroup, Intent } from '@blueprintjs/core';
-
-interface Props {
-  transformation: any;
-  setTransformation: React.Dispatch<React.SetStateAction<{}>>;
-}
-
-export const IssueTracking = ({ transformation, setTransformation }: Props) => {
-  return (
-    <>
-      <h3>
-        <span>Issue Tracking</span>
-        <Tag minimal>RegExp</Tag>
-      </h3>
-      <p>
-        Map your issue labels with each category to view corresponding metrics in the dashboard.
-        <a
-          href="https://devlake.apache.org/docs/UserManuals/ConfigUI/GitHub/#issue-tracking"
-          rel="noreferrer"
-          target="_blank"
-        >
-          See doc
-        </a>
-      </p>
-      <FormGroup inline label="Severity">
-        <InputGroup
-          placeholder="severity/(.*)$"
-          value={transformation.issueSeverity}
-          onChange={(e) =>
-            setTransformation({
-              ...transformation,
-              issueSeverity: e.target.value,
-            })
-          }
-        />
-      </FormGroup>
-      <FormGroup inline label="Component">
-        <InputGroup
-          placeholder="component/(.*)$"
-          value={transformation.issueComponent}
-          onChange={(e) =>
-            setTransformation({
-              ...transformation,
-              issueComponent: e.target.value,
-            })
-          }
-        />
-      </FormGroup>
-      <FormGroup inline label="Priority">
-        <InputGroup
-          placeholder="(highest|high|medium|low)$"
-          value={transformation.issuePriority}
-          onChange={(e) =>
-            setTransformation({
-              ...transformation,
-              issuePriority: e.target.value,
-            })
-          }
-        />
-      </FormGroup>
-      <FormGroup inline label="Type/Requirement">
-        <InputGroup
-          placeholder="(feat|feature|proposal|requirement)$"
-          value={transformation.issueTypeRequirement}
-          onChange={(e) =>
-            setTransformation({
-              ...transformation,
-              issueTypeRequirement: e.target.value,
-            })
-          }
-        />
-      </FormGroup>
-      <FormGroup inline label="Type/Bug">
-        <InputGroup
-          placeholder="(bug|broken)$"
-          value={transformation.issueTypeBug}
-          onChange={(e) =>
-            setTransformation({
-              ...transformation,
-              issueTypeBug: e.target.value,
-            })
-          }
-        />
-      </FormGroup>
-      <FormGroup
-        inline
-        label={
-          <span>
-            Type/Incident
-            <Tag minimal intent={Intent.PRIMARY} style={{ marginLeft: 4, fontSize: 10 }}>
-              DORA
-            </Tag>
-          </span>
-        }
-      >
-        <InputGroup
-          placeholder="(incident|p0|p1|p2)$"
-          value={transformation.issueTypeIncident}
-          onChange={(e) =>
-            setTransformation({
-              ...transformation,
-              issueTypeIncident: e.target.value,
-            })
-          }
-        />
-      </FormGroup>
-    </>
-  );
-};
diff --git a/config-ui/src/plugins/register/github/styled.ts b/config-ui/src/plugins/register/github/styled.ts
index 11365fb72..876b88901 100644
--- a/config-ui/src/plugins/register/github/styled.ts
+++ b/config-ui/src/plugins/register/github/styled.ts
@@ -19,16 +19,59 @@
 import styled from 'styled-components';
 
 export const TransformationWrapper = styled.div`
-  h3 {
-    margin: 0 0 8px;
+  .issue-tracking {
+    .issue-type {
+      .title {
+        margin-bottom: 8px;
+      }
 
-    .bp4-tag {
-      margin-left: 6px;
+      .list {
+        padding-left: 40px;
+      }
     }
   }
 
-  p {
-    margin: 0 0 8px;
+  .ci-cd {
+    h3 {
+      margin-top: 16px;
+      margin-bottom: 8px;
+    }
+
+    .radio {
+      padding-left: 20px;
+    }
+  }
+
+  .additional-settings {
+    h2 {
+      display: flex;
+      align-items: center;
+      cursor: pointer;
+    }
+
+    .radio {
+      display: flex;
+      align-items: center;
+      margin: 8px 0 16px;
+
+      p {
+        margin: 0;
+      }
+
+      .bp4-control {
+        margin: 0;
+      }
+    }
+
+    .refdiff {
+      display: flex;
+      align-items: center;
+      padding-left: 20px;
+
+      .bp4-input-group {
+        margin: 0 8px;
+      }
+    }
   }
 
   .bp4-form-group {
@@ -36,7 +79,7 @@ export const TransformationWrapper = styled.div`
     align-items: center;
 
     .bp4-label {
-      flex: 0 0 150px;
+      flex: 0 0 140px;
     }
 
     .bp4-form-content {
diff --git a/config-ui/src/plugins/register/github/transformation.tsx b/config-ui/src/plugins/register/github/transformation.tsx
index 988af05fa..b61abb719 100644
--- a/config-ui/src/plugins/register/github/transformation.tsx
+++ b/config-ui/src/plugins/register/github/transformation.tsx
@@ -16,28 +16,406 @@
  *
  */
 
-import React from 'react';
+import React, { useState } from 'react';
+import {
+  FormGroup,
+  InputGroup,
+  TextArea,
+  Tag,
+  RadioGroup,
+  Radio,
+  Icon,
+  Collapse,
+  Checkbox,
+  Intent,
+  Colors,
+} from '@blueprintjs/core';
 
-import { Divider } from '@/components';
+import { ExternalLink, HelpTooltip, Divider } from '@/components';
 
 import * as S from './styled';
 
-import { IssueTracking, CiCd, CodeReview, AdditionalSettings } from './components';
-
 interface Props {
   transformation: any;
   setTransformation: React.Dispatch<React.SetStateAction<any>>;
 }
 
-export const GitHubTransformation = ({ ...props }: Props) => {
+export const GitHubTransformation = ({ transformation, setTransformation }: Props) => {
+  const [enableCICD, setEnableCICD] = useState(1);
+  const [openAdditionalSettings, setOpenAdditionalSettings] = useState(false);
+
+  const handleChangeCICDEnable = (e: number) => {
+    if (e === 0) {
+      setTransformation({
+        ...transformation,
+        deploymentPattern: undefined,
+        productionPattern: undefined,
+      });
+    } else {
+      setTransformation({
+        ...transformation,
+        deploymentPattern: '',
+        productionPattern: '',
+      });
+    }
+    setEnableCICD(e);
+  };
+
+  const handleChangeAdditionalSettingsOpen = () => {
+    setOpenAdditionalSettings(!openAdditionalSettings);
+    if (!openAdditionalSettings) {
+      setTransformation({
+        ...transformation,
+        refdiff: null,
+      });
+    }
+  };
+
   return (
     <S.TransformationWrapper>
-      <IssueTracking {...props} />
+      {/* Issue Tracking */}
+      <div className="issue-tracking">
+        <h2>Issue Tracking</h2>
+        <p>
+          Tell DevLake what your issue labels mean to view metrics such as{' '}
+          <ExternalLink link="https://devlake.apache.org/docs/Metrics/BugAge">Bug Age</ExternalLink>,{' '}
+          <ExternalLink link="https://devlake.apache.org/docs/Metrics/MTTR">
+            DORA - Median Time to Restore Service
+          </ExternalLink>
+          , etc.
+        </p>
+        <div className="issue-type">
+          <div className="title">
+            <span>Issue Type</span>
+            <HelpTooltip content="DevLake defines three standard types of issues: FEATURE, BUG and INCIDENT. Set your issues to these three types with issue labels that match the RegEx." />
+          </div>
+          <div className="list">
+            <FormGroup inline label="Feature">
+              <InputGroup
+                placeholder="(feat|feature|proposal|requirement)$"
+                value={transformation.issueTypeRequirement}
+                onChange={(e) =>
+                  setTransformation({
+                    ...transformation,
+                    issueTypeRequirement: e.target.value,
+                  })
+                }
+              />
+            </FormGroup>
+            <FormGroup inline label="Bug">
+              <InputGroup
+                placeholder="(bug|broken)$"
+                value={transformation.issueTypeBug}
+                onChange={(e) =>
+                  setTransformation({
+                    ...transformation,
+                    issueTypeBug: e.target.value,
+                  })
+                }
+              />
+            </FormGroup>
+            <FormGroup
+              inline
+              label={
+                <span>
+                  Incident
+                  <Tag minimal intent={Intent.PRIMARY} style={{ marginLeft: 4 }}>
+                    DORA
+                  </Tag>
+                </span>
+              }
+            >
+              <InputGroup
+                placeholder="(incident|p0|p1|p2)$"
+                value={transformation.issueTypeIncident}
+                onChange={(e) =>
+                  setTransformation({
+                    ...transformation,
+                    issueTypeIncident: e.target.value,
+                  })
+                }
+              />
+            </FormGroup>
+          </div>
+        </div>
+        <FormGroup
+          inline
+          label={
+            <>
+              <span>Issue Priority</span>
+              <HelpTooltip content="Labels that match the RegEx will be set as the priority of an issue." />
+            </>
+          }
+        >
+          <InputGroup
+            placeholder="(highest|high|medium|low)$"
+            value={transformation.issuePriority}
+            onChange={(e) =>
+              setTransformation({
+                ...transformation,
+                issuePriority: e.target.value,
+              })
+            }
+          />
+        </FormGroup>
+        <FormGroup
+          inline
+          label={
+            <>
+              <span>Issue Component</span>
+              <HelpTooltip content="Labels that match the RegEx will be set as the component of an issue." />
+            </>
+          }
+        >
+          <InputGroup
+            placeholder="component/(.*)$"
+            value={transformation.issueComponent}
+            onChange={(e) =>
+              setTransformation({
+                ...transformation,
+                issueComponent: e.target.value,
+              })
+            }
+          />
+        </FormGroup>
+        <FormGroup
+          inline
+          label={
+            <>
+              <span>Issue Severity</span>
+              <HelpTooltip content="Labels that match the RegEx will be set as the serverity of an issue." />
+            </>
+          }
+        >
+          <InputGroup
+            placeholder="severity/(.*)$"
+            value={transformation.issueSeverity}
+            onChange={(e) =>
+              setTransformation({
+                ...transformation,
+                issueSeverity: e.target.value,
+              })
+            }
+          />
+        </FormGroup>
+      </div>
+      <Divider />
+      {/* CI/CD */}
+      <div className="ci-cd">
+        <h2>CI/CD</h2>
+        <h3>
+          <span>Deployment</span>
+          <Tag minimal intent={Intent.PRIMARY} style={{ marginLeft: 4, fontWeight: 400 }}>
+            DORA
+          </Tag>
+        </h3>
+        <p>Tell DevLake what CI jobs are Deployments.</p>
+        <RadioGroup
+          selectedValue={enableCICD}
+          onChange={(e) => handleChangeCICDEnable(+(e.target as HTMLInputElement).value)}
+        >
+          <Radio label="Detect Deployment from Jobs in GitHub Action" value={1} />
+          {enableCICD === 1 && (
+            <>
+              <p>
+                Not sure what a GitHub Action is?{' '}
+                <ExternalLink link="https://docs.github.com/en/actions/using-jobs/using-jobs-in-a-workflow">
+                  See it here
+                </ExternalLink>
+              </p>
+              <div className="radio">
+                <FormGroup
+                  inline
+                  label={
+                    <>
+                      <span>Deployment</span>
+                      <HelpTooltip content="A GitHub Action job with a name that matches the RegEx will be considered as a deployment in DevLake." />
+                    </>
+                  }
+                >
+                  <InputGroup
+                    placeholder="(?i)deploy"
+                    value={transformation.deploymentPattern}
+                    onChange={(e) =>
+                      setTransformation({
+                        ...transformation,
+                        deploymentPattern: e.target.value,
+                      })
+                    }
+                  />
+                </FormGroup>
+                <FormGroup
+                  inline
+                  label={
+                    <>
+                      <span>Production</span>
+                      <HelpTooltip content="DevLake is only concerned with deployments in production environment when calculating DORA metrics.A GitHub Action job with a name that matches the given RegEx will be considered as a job in the Production environment. If you leave this field empty, all data will be tagged as in the Production environment." />
+                    </>
+                  }
+                >
+                  <InputGroup
+                    placeholder="(?i)production"
+                    value={transformation.productionPattern}
+                    onChange={(e) =>
+                      setTransformation({
+                        ...transformation,
+                        productionPattern: e.target.value,
+                      })
+                    }
+                  />
+                </FormGroup>
+              </div>
+            </>
+          )}
+          <Radio label="Not using any GitHub entities as Deployment" value={0} />
+        </RadioGroup>
+      </div>
+      <Divider />
+      {/* Code Review */}
+      <div>
+        <h2>Code Review</h2>
+        <p>
+          If you use labels to identify types and components of pull requests, use the following RegExes to extract them
+          into corresponding columns.{' '}
+          <ExternalLink link="https://devlake.apache.org/docs/DataModels/DevLakeDomainLayerSchema#pull_requests">
+            Learn More
+          </ExternalLink>
+        </p>
+        <FormGroup
+          inline
+          label={
+            <>
+              <span>PR Type</span>
+              <HelpTooltip content="Labels that match the RegEx will be set as the type of a pull request." />
+            </>
+          }
+        >
+          <InputGroup
+            placeholder="type/(.*)$"
+            value={transformation.prType}
+            onChange={(e) => setTransformation({ ...transformation, prType: e.target.value })}
+          />
+        </FormGroup>
+        <FormGroup
+          inline
+          label={
+            <>
+              <span>PR Component</span>
+              <HelpTooltip content="Labels that match the RegEx will be set as the component of a pull request." />
+            </>
+          }
+        >
+          <InputGroup
+            placeholder="component/(.*)$"
+            value={transformation.prComponent}
+            onChange={(e) =>
+              setTransformation({
+                ...transformation,
+                prComponent: e.target.value,
+              })
+            }
+          />
+        </FormGroup>
+      </div>
       <Divider />
-      <CiCd {...props} />
+      {/* Cross-domain */}
+      <div>
+        <h2>Cross-domain</h2>
+        <p>
+          Connect entities across domains to measure metrics such as{' '}
+          <ExternalLink link="https://devlake.apache.org/docs/Metrics/BugCountPer1kLinesOfCode">
+            Bug Count per 1k Lines of Code
+          </ExternalLink>
+          .
+        </p>
+        <FormGroup
+          inline
+          label={
+            <div className="label">
+              <span>Connect PRs and Issues</span>
+              <HelpTooltip
+                content={
+                  <>
+                    <div>
+                      <Icon icon="tick-circle" size={12} color={Colors.GREEN4} style={{ marginRight: '4px' }} />
+                      Example 1: PR #321 body contains "<strong>Closes #1234</strong>" (PR #321 and issue #1234 will be
+                      mapped by the following RegEx)
+                    </div>
+                    <div>
+                      <Icon icon="delete" size={12} color={Colors.RED4} style={{ marginRight: '4px' }} />
+                      Example 2: PR #321 body contains "<strong>Related to #1234</strong>" (PR #321 and issue #1234 will
+                      NOT be mapped by the following RegEx)
+                    </div>
+                  </>
+                }
+              />
+            </div>
+          }
+        >
+          <TextArea
+            value={transformation.prBodyClosePattern}
+            placeholder="(?mi)(fix|close|resolve|fixes|closes|resolves|fixed|closed|resolved)[\s]*.*(((and )?(#|https:\/\/github.com\/%s\/issues\/)\d+[ ]*)+)"
+            onChange={(e) =>
+              setTransformation({
+                ...transformation,
+                prBodyClosePattern: e.target.value,
+              })
+            }
+            fill
+            rows={2}
+          />
+        </FormGroup>
+      </div>
       <Divider />
-      <CodeReview {...props} />
-      <AdditionalSettings {...props} />
+      {/* Additional Settings */}
+      <div className="additional-settings">
+        <h2 onClick={handleChangeAdditionalSettingsOpen}>
+          <Icon icon={openAdditionalSettings ? 'chevron-up' : 'chevron-down'} size={18} />
+          <span>Additional Settings</span>
+        </h2>
+        <Collapse isOpen={openAdditionalSettings}>
+          <div className="radio">
+            <Radio checked />
+            <p>
+              Enable the <ExternalLink link="https://devlake.apache.org/docs/Plugins/refdiff">RefDiff</ExternalLink>{' '}
+              plugin to pre-calculate version-based metrics
+              <HelpTooltip content="Calculate the commits diff between two consecutive tags that match the following RegEx. Issues closed by PRs which contain these commits will also be calculated. The result will be shown in table.refs_commits_diffs and table.refs_issues_diffs." />
+            </p>
+          </div>
+          <div className="refdiff">
+            Compare the last
+            <InputGroup
+              style={{ width: 60 }}
+              value={transformation.refdiff?.tagsOrder}
+              onChange={(e) =>
+                setTransformation({
+                  ...transformation,
+                  refdiff: {
+                    ...transformation?.refdiff,
+                    tagsOrder: e.target.value,
+                  },
+                })
+              }
+            />
+            tags that match the
+            <InputGroup
+              style={{ width: 200 }}
+              placeholder="(regex)$"
+              value={transformation.refdiff?.tagsPattern}
+              onChange={(e) =>
+                setTransformation({
+                  ...transformation,
+                  refdiff: {
+                    ...transformation?.refdiff,
+                    tagsPattern: e.target.value,
+                  },
+                })
+              }
+            />
+            for calculation
+          </div>
+        </Collapse>
+      </div>
     </S.TransformationWrapper>
   );
 };