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 c34803873 feat(config-ui): add new pages about transformation (#4052)
c34803873 is described below

commit c3480387346b43dba36a7cd10c22a9d9b6d7b2be
Author: 青湛 <[email protected]>
AuthorDate: Wed Dec 28 19:30:19 2022 +0800

    feat(config-ui): add new pages about transformation (#4052)
    
    * refactor(config-ui): adjust the style for plugin transformation
    
    * feat(config-ui): add global transformation store
    
    * feat(config-ui): add new page transformations
    
    * feat(config-ui): add new page transformation detail
    
    * feat(config-ui): add menu and routes for transformation
---
 config-ui/src/App.js                               |  19 ++-
 config-ui/src/layouts/base/use-menu.ts             |   6 +
 config-ui/src/pages/index.ts                       |   1 +
 .../transformation/detail/api.ts}                  |  27 ++--
 .../src/pages/transformation/detail/index.tsx      | 119 ++++++++++++++++
 .../transformation/detail/styled.ts}               |  33 +++--
 .../src/pages/transformation/detail/use-detail.ts  |  91 ++++++++++++
 config-ui/src/pages/transformation/home/index.tsx  | 157 +++++++++++++++++++++
 .../transformation/home/styled.ts}                 |  32 +++--
 .../src/{store => pages/transformation}/index.ts   |   4 +-
 .../transformation.tsx => github/styled.ts}        |  38 +++--
 config-ui/src/plugins/github/transformation.tsx    |   6 +-
 .../gitlab/{transformation.tsx => styled.ts}       |  38 +++--
 config-ui/src/plugins/gitlab/transformation.tsx    |   5 +-
 .../transformation.tsx => jenkins/styled.ts}       |  38 +++--
 config-ui/src/plugins/jenkins/transformation.tsx   |   5 +-
 .../{gitlab/transformation.tsx => jira/styled.ts}  |  38 +++--
 config-ui/src/plugins/jira/transformation.tsx      |   5 +-
 config-ui/src/store/index.ts                       |   3 +-
 .../index.ts => store/transformations/api.ts}      |  10 +-
 config-ui/src/store/transformations/context.tsx    |  61 ++++++++
 config-ui/src/store/{ => transformations}/index.ts |   4 +-
 .../index.ts => store/transformations/types.ts}    |  11 +-
 .../src/store/transformations/use-context-value.ts |  79 +++++++++++
 24 files changed, 717 insertions(+), 113 deletions(-)

diff --git a/config-ui/src/App.js b/config-ui/src/App.js
index 9e0d779c2..06999a60d 100644
--- a/config-ui/src/App.js
+++ b/config-ui/src/App.js
@@ -32,7 +32,9 @@ import {
   WebHookConnectionPage,
   CreateBlueprintPage,
   BlueprintHomePage,
-  BlueprintDetailPage
+  BlueprintDetailPage,
+  TransformationHomePage,
+  TransformationDetailPage
 } from '@/pages'
 import ManageIntegration from '@/pages/configure/integration/manage'
 import AddConnection from '@/pages/configure/connections/AddConnection'
@@ -94,6 +96,21 @@ function App() {
           path='/blueprints/:id'
           component={() => <BlueprintDetailPage />}
         />
+        <Route
+          exact
+          path='/transformations'
+          component={() => <TransformationHomePage />}
+        />
+        <Route
+          exact
+          path='/transformations/:plugin/create'
+          component={() => <TransformationDetailPage />}
+        />
+        <Route
+          exact
+          path='/transformations/:plugin/:tid'
+          component={() => <TransformationDetailPage />}
+        />
       </Switch>
     </BaseLayout>
   )
diff --git a/config-ui/src/layouts/base/use-menu.ts 
b/config-ui/src/layouts/base/use-menu.ts
index fef4a2b8c..fcc43f9e6 100644
--- a/config-ui/src/layouts/base/use-menu.ts
+++ b/config-ui/src/layouts/base/use-menu.ts
@@ -80,6 +80,12 @@ export const useMenu = () => {
             }
           ]
         },
+        {
+          key: 'transformation',
+          title: 'Transformation',
+          icon: 'function',
+          path: '/transformations'
+        },
         {
           key: 'dashboard',
           title: 'Dashboard',
diff --git a/config-ui/src/pages/index.ts b/config-ui/src/pages/index.ts
index 405539b29..752a0091e 100644
--- a/config-ui/src/pages/index.ts
+++ b/config-ui/src/pages/index.ts
@@ -20,3 +20,4 @@ export * from './project'
 export * from './connection'
 export * from './blueprint'
 export * from './pipeline'
+export * from './transformation'
diff --git a/config-ui/src/plugins/gitlab/transformation.tsx 
b/config-ui/src/pages/transformation/detail/api.ts
similarity index 59%
copy from config-ui/src/plugins/gitlab/transformation.tsx
copy to config-ui/src/pages/transformation/detail/api.ts
index 6f9402e83..44126ee89 100644
--- a/config-ui/src/plugins/gitlab/transformation.tsx
+++ b/config-ui/src/pages/transformation/detail/api.ts
@@ -16,19 +16,20 @@
  *
  */
 
-import React from 'react'
+import request from '@/components/utils/request'
+import { Plugins } from '@/plugins'
 
-import { CiCd } from './components'
+export const createTransformation = (plugin: Plugins, payload: any) =>
+  request(`/plugins/${plugin}/transformation_rules`, {
+    method: 'post',
+    data: payload
+  })
 
-interface Props {
-  transformation: any
-  setTransformation: React.Dispatch<React.SetStateAction<any>>
-}
+export const getTransformation = (plugin: Plugins, id: ID) =>
+  request(`/plugins/${plugin}/transformation_rules/${id}`)
 
-export const GitLabTransformation = ({ ...props }: Props) => {
-  return (
-    <>
-      <CiCd {...props} />
-    </>
-  )
-}
+export const updateTransformation = (plugin: Plugins, id: ID, payload: any) =>
+  request(`/plugins/${plugin}/transformation_rules/${id}`, {
+    method: 'patch',
+    data: payload
+  })
diff --git a/config-ui/src/pages/transformation/detail/index.tsx 
b/config-ui/src/pages/transformation/detail/index.tsx
new file mode 100644
index 000000000..4547a6102
--- /dev/null
+++ b/config-ui/src/pages/transformation/detail/index.tsx
@@ -0,0 +1,119 @@
+/*
+ * 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 { useParams, useHistory } from 'react-router-dom'
+import { InputGroup, ButtonGroup, Button, Intent } from '@blueprintjs/core'
+
+import { PageLoading, PageHeader, Card } from '@/components'
+import { Plugins } from '@/plugins'
+import { GitHubTransformation } from '@/plugins/github'
+import { GitLabTransformation } from '@/plugins/gitlab'
+import { JenkinsTransformation } from '@/plugins/jenkins'
+
+import { useDetail } from './use-detail'
+import * as S from './styled'
+
+export const TransformationDetailPage = () => {
+  const { plugin, tid } = useParams<{ plugin: Plugins; tid?: string }>()
+  const history = useHistory()
+
+  const {
+    loading,
+    operating,
+    name,
+    transformation,
+    onChangeName,
+    onChangeTransformation,
+    onSave
+  } = useDetail({ plugin, id: tid })
+
+  if (loading) {
+    return <PageLoading />
+  }
+
+  return (
+    <PageHeader
+      breadcrumbs={[
+        { name: 'Transformations', path: '/transformations' },
+        {
+          name: plugin,
+          path: '/transformations'
+        },
+        {
+          name: 'Create',
+          path: `/transformations/${plugin}/${tid ? tid : 'Create'}`
+        }
+      ]}
+    >
+      <S.Wrapper>
+        <Card className='name card'>
+          <h3>Transformation Name *</h3>
+          <p>
+            Give this set of transformation rules a unique name so that you can
+            identify it in the future.
+          </p>
+          <InputGroup
+            placeholder='Enter Transformation Name'
+            value={name}
+            onChange={(e) => onChangeName(e.target.value)}
+          />
+        </Card>
+        <Card className='card'>
+          {plugin === Plugins.GitHub && (
+            <GitHubTransformation
+              transformation={transformation}
+              setTransformation={onChangeTransformation}
+            />
+          )}
+          {plugin === Plugins.GitLab && (
+            <GitLabTransformation
+              transformation={transformation}
+              setTransformation={onChangeTransformation}
+            />
+          )}
+
+          {plugin === Plugins.Jenkins && (
+            <JenkinsTransformation
+              transformation={transformation}
+              setTransformation={onChangeTransformation}
+            />
+          )}
+          <div className='action'>
+            <ButtonGroup>
+              <Button
+                disabled={operating}
+                outlined
+                text='Cancel'
+                onClick={() => history.push('/transformations')}
+              />
+              <Button
+                disabled={!name}
+                loading={operating}
+                outlined
+                intent={Intent.PRIMARY}
+                text='Save'
+                onClick={onSave}
+              />
+            </ButtonGroup>
+          </div>
+        </Card>
+      </S.Wrapper>
+    </PageHeader>
+  )
+}
diff --git a/config-ui/src/plugins/gitlab/transformation.tsx 
b/config-ui/src/pages/transformation/detail/styled.ts
similarity index 73%
copy from config-ui/src/plugins/gitlab/transformation.tsx
copy to config-ui/src/pages/transformation/detail/styled.ts
index 6f9402e83..6942ebbae 100644
--- a/config-ui/src/plugins/gitlab/transformation.tsx
+++ b/config-ui/src/pages/transformation/detail/styled.ts
@@ -16,19 +16,26 @@
  *
  */
 
-import React from 'react'
+import styled from 'styled-components'
 
-import { CiCd } from './components'
+export const Wrapper = styled.div`
+  .card + .card {
+    margin-top: 32px;
+  }
 
-interface Props {
-  transformation: any
-  setTransformation: React.Dispatch<React.SetStateAction<any>>
-}
+  .name {
+    h3 {
+      margin: 0 0 8px;
+    }
 
-export const GitLabTransformation = ({ ...props }: Props) => {
-  return (
-    <>
-      <CiCd {...props} />
-    </>
-  )
-}
+    p {
+      margin: 0 0 8px;
+    }
+  }
+
+  .action {
+    display: flex;
+    justify-content: flex-end;
+    margin-top: 16px;
+  }
+`
diff --git a/config-ui/src/pages/transformation/detail/use-detail.ts 
b/config-ui/src/pages/transformation/detail/use-detail.ts
new file mode 100644
index 000000000..8e8cc1ae2
--- /dev/null
+++ b/config-ui/src/pages/transformation/detail/use-detail.ts
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { useState, useEffect, useMemo } from 'react'
+import { useHistory } from 'react-router-dom'
+
+import { Plugins, PluginConfig } from '@/plugins'
+import { operator } from '@/utils'
+
+import * as API from './api'
+
+interface Props {
+  plugin: Plugins
+  id?: ID
+}
+
+export const useDetail = ({ plugin, id }: Props) => {
+  const [loading, setLoading] = useState(false)
+  const [operating, setOperating] = useState(false)
+  const [name, setName] = useState('')
+  const [transformation, setTransformation] = useState<any>(
+    PluginConfig.find((pc) => pc.plugin === plugin)?.transformation
+  )
+
+  const history = useHistory()
+
+  const getTransformation = async () => {
+    if (!id) return
+    setLoading(true)
+    try {
+      const res = await API.getTransformation(plugin, id)
+      setName(res.name)
+      setTransformation(res)
+    } finally {
+      setLoading(false)
+    }
+  }
+
+  useEffect(() => {
+    getTransformation()
+  }, [])
+
+  const handleSave = async () => {
+    const payload = {
+      ...transformation,
+      name
+    }
+
+    const [success] = await operator(
+      () =>
+        id
+          ? API.updateTransformation(plugin, id, payload)
+          : API.createTransformation(plugin, payload),
+      {
+        setOperating
+      }
+    )
+
+    if (success) {
+      history.push('/transformations')
+    }
+  }
+
+  return useMemo(
+    () => ({
+      loading,
+      operating,
+      name,
+      transformation,
+      onChangeName: setName,
+      onChangeTransformation: setTransformation,
+      onSave: handleSave
+    }),
+    [loading, operating, name, transformation]
+  )
+}
diff --git a/config-ui/src/pages/transformation/home/index.tsx 
b/config-ui/src/pages/transformation/home/index.tsx
new file mode 100644
index 000000000..899a83c21
--- /dev/null
+++ b/config-ui/src/pages/transformation/home/index.tsx
@@ -0,0 +1,157 @@
+/*
+ * 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, useMemo } from 'react'
+import { useHistory } from 'react-router-dom'
+import { ButtonGroup, Button, Icon, Intent } from '@blueprintjs/core'
+
+import { PageHeader, Table, ColumnType, Dialog, Selector } from '@/components'
+import type { PluginConfigType } from '@/plugins'
+import { Plugins } from '@/plugins'
+import {
+  TransformationContextProvider,
+  TransformationContextConsumer,
+  TransformationItemType
+} from '@/store'
+
+import * as S from './styled'
+
+export const TransformationHomePage = () => {
+  const [active, setActive] = useState('All')
+  const [isOpen, setIsOpen] = useState(false)
+  const [selectedPlugin, setSelectedPlugin] = useState<PluginConfigType>()
+
+  const history = useHistory()
+
+  const columns = useMemo(
+    () =>
+      [
+        {
+          title: 'Name',
+          dataIndex: 'name',
+          key: 'name'
+        },
+        {
+          title: 'Data Source',
+          dataIndex: 'plugin',
+          key: 'plugin'
+        },
+        {
+          title: '',
+          key: 'action',
+          align: 'center',
+          render: (_, row) =>
+            row.plugin !== Plugins.JIRA && (
+              <Button
+                minimal
+                intent={Intent.PRIMARY}
+                icon='cog'
+                onClick={() =>
+                  history.push(`/transformations/${row.plugin}/${row.id}`)
+                }
+              />
+            )
+        }
+      ] as ColumnType<TransformationItemType>,
+    []
+  )
+
+  return (
+    <TransformationContextProvider>
+      <TransformationContextConsumer>
+        {({ plugins, transformations }) => (
+          <PageHeader
+            breadcrumbs={[
+              { name: 'Transformations', path: '/transformations' }
+            ]}
+          >
+            <S.Wrapper>
+              <div className='action'>
+                <ButtonGroup>
+                  <Button
+                    intent={active === 'All' ? Intent.PRIMARY : Intent.NONE}
+                    text='All'
+                    onClick={() => setActive('All')}
+                  />
+                  {plugins.map((p) => (
+                    <Button
+                      key={p.plugin}
+                      intent={
+                        active === p.plugin ? Intent.PRIMARY : Intent.NONE
+                      }
+                      text={p.name}
+                      onClick={() => setActive(p.plugin)}
+                    />
+                  ))}
+                </ButtonGroup>
+                <Button
+                  intent={Intent.PRIMARY}
+                  text='Create Transfromation'
+                  onClick={() => setIsOpen(true)}
+                />
+              </div>
+              <Table
+                columns={columns}
+                dataSource={transformations.filter((ts) =>
+                  active === 'All' ? true : ts.plugin === active
+                )}
+              />
+              <Dialog
+                isOpen={isOpen}
+                title='Select a Data Source'
+                okText='Continue'
+                okDisabled={
+                  !selectedPlugin || selectedPlugin.plugin === Plugins.JIRA
+                }
+                onOk={() =>
+                  history.push(
+                    `/transformations/${selectedPlugin?.plugin}/create`
+                  )
+                }
+                onCancel={() => setIsOpen(false)}
+              >
+                <S.DialogWrapper>
+                  <p>Select from the supported data sources</p>
+                  <Selector
+                    items={plugins}
+                    getKey={(it) => it.plugin}
+                    getName={(it) => it.name}
+                    selectedItem={selectedPlugin}
+                    onChangeItem={(selectedItem) =>
+                      setSelectedPlugin(selectedItem)
+                    }
+                  />
+                  {selectedPlugin?.plugin === Plugins.JIRA && (
+                    <div className='warning'>
+                      <Icon icon='error' />
+                      <span>
+                        Because Jira transformation is specific to every Jira
+                        connection, you can only add a Transformation in
+                        Blueprints.
+                      </span>
+                    </div>
+                  )}
+                </S.DialogWrapper>
+              </Dialog>
+            </S.Wrapper>
+          </PageHeader>
+        )}
+      </TransformationContextConsumer>
+    </TransformationContextProvider>
+  )
+}
diff --git a/config-ui/src/plugins/gitlab/transformation.tsx 
b/config-ui/src/pages/transformation/home/styled.ts
similarity index 66%
copy from config-ui/src/plugins/gitlab/transformation.tsx
copy to config-ui/src/pages/transformation/home/styled.ts
index 6f9402e83..74e40e624 100644
--- a/config-ui/src/plugins/gitlab/transformation.tsx
+++ b/config-ui/src/pages/transformation/home/styled.ts
@@ -16,19 +16,25 @@
  *
  */
 
-import React from 'react'
+import { Colors } from '@blueprintjs/core'
+import styled from 'styled-components'
 
-import { CiCd } from './components'
+export const Wrapper = styled.div`
+  .action {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 16px;
+  }
+`
 
-interface Props {
-  transformation: any
-  setTransformation: React.Dispatch<React.SetStateAction<any>>
-}
+export const DialogWrapper = styled.div`
+  .warning {
+    margin-top: 16px;
 
-export const GitLabTransformation = ({ ...props }: Props) => {
-  return (
-    <>
-      <CiCd {...props} />
-    </>
-  )
-}
+    .bp4-icon {
+      color: ${Colors.ORANGE3};
+      margin-right: 4px;
+    }
+  }
+`
diff --git a/config-ui/src/store/index.ts 
b/config-ui/src/pages/transformation/index.ts
similarity index 93%
copy from config-ui/src/store/index.ts
copy to config-ui/src/pages/transformation/index.ts
index 6199fc0b7..188478fcd 100644
--- a/config-ui/src/store/index.ts
+++ b/config-ui/src/pages/transformation/index.ts
@@ -16,5 +16,5 @@
  *
  */
 
-export * from './connections'
-export * from './version'
+export * from './home'
+export * from './detail'
diff --git a/config-ui/src/plugins/gitlab/transformation.tsx 
b/config-ui/src/plugins/github/styled.ts
similarity index 69%
copy from config-ui/src/plugins/gitlab/transformation.tsx
copy to config-ui/src/plugins/github/styled.ts
index 6f9402e83..6da07d584 100644
--- a/config-ui/src/plugins/gitlab/transformation.tsx
+++ b/config-ui/src/plugins/github/styled.ts
@@ -16,19 +16,31 @@
  *
  */
 
-import React from 'react'
+import styled from 'styled-components'
 
-import { CiCd } from './components'
+export const TransformationWrapper = styled.div`
+  h3 {
+    margin: 0 0 8px;
 
-interface Props {
-  transformation: any
-  setTransformation: React.Dispatch<React.SetStateAction<any>>
-}
+    .bp4-tag {
+      margin-left: 6px;
+    }
+  }
 
-export const GitLabTransformation = ({ ...props }: Props) => {
-  return (
-    <>
-      <CiCd {...props} />
-    </>
-  )
-}
+  p {
+    margin: 0 0 8px;
+  }
+
+  .bp4-form-group {
+    display: flex;
+    align-items: center;
+
+    .bp4-label {
+      flex: 0 0 150px;
+    }
+
+    .bp4-form-content {
+      flex: auto;
+    }
+  }
+`
diff --git a/config-ui/src/plugins/github/transformation.tsx 
b/config-ui/src/plugins/github/transformation.tsx
index e3c64cf35..e2c5cb60e 100644
--- a/config-ui/src/plugins/github/transformation.tsx
+++ b/config-ui/src/plugins/github/transformation.tsx
@@ -20,6 +20,8 @@ import React from 'react'
 
 import { Divider } from '@/components'
 
+import * as S from './styled'
+
 import {
   IssueTracking,
   CiCd,
@@ -34,13 +36,13 @@ interface Props {
 
 export const GitHubTransformation = ({ ...props }: Props) => {
   return (
-    <>
+    <S.TransformationWrapper>
       <IssueTracking {...props} />
       <Divider />
       <CiCd {...props} />
       <Divider />
       <CodeReview {...props} />
       <AdditionalSettings {...props} />
-    </>
+    </S.TransformationWrapper>
   )
 }
diff --git a/config-ui/src/plugins/gitlab/transformation.tsx 
b/config-ui/src/plugins/gitlab/styled.ts
similarity index 69%
copy from config-ui/src/plugins/gitlab/transformation.tsx
copy to config-ui/src/plugins/gitlab/styled.ts
index 6f9402e83..6da07d584 100644
--- a/config-ui/src/plugins/gitlab/transformation.tsx
+++ b/config-ui/src/plugins/gitlab/styled.ts
@@ -16,19 +16,31 @@
  *
  */
 
-import React from 'react'
+import styled from 'styled-components'
 
-import { CiCd } from './components'
+export const TransformationWrapper = styled.div`
+  h3 {
+    margin: 0 0 8px;
 
-interface Props {
-  transformation: any
-  setTransformation: React.Dispatch<React.SetStateAction<any>>
-}
+    .bp4-tag {
+      margin-left: 6px;
+    }
+  }
 
-export const GitLabTransformation = ({ ...props }: Props) => {
-  return (
-    <>
-      <CiCd {...props} />
-    </>
-  )
-}
+  p {
+    margin: 0 0 8px;
+  }
+
+  .bp4-form-group {
+    display: flex;
+    align-items: center;
+
+    .bp4-label {
+      flex: 0 0 150px;
+    }
+
+    .bp4-form-content {
+      flex: auto;
+    }
+  }
+`
diff --git a/config-ui/src/plugins/gitlab/transformation.tsx 
b/config-ui/src/plugins/gitlab/transformation.tsx
index 6f9402e83..58a2192be 100644
--- a/config-ui/src/plugins/gitlab/transformation.tsx
+++ b/config-ui/src/plugins/gitlab/transformation.tsx
@@ -19,6 +19,7 @@
 import React from 'react'
 
 import { CiCd } from './components'
+import * as S from './styled'
 
 interface Props {
   transformation: any
@@ -27,8 +28,8 @@ interface Props {
 
 export const GitLabTransformation = ({ ...props }: Props) => {
   return (
-    <>
+    <S.TransformationWrapper>
       <CiCd {...props} />
-    </>
+    </S.TransformationWrapper>
   )
 }
diff --git a/config-ui/src/plugins/gitlab/transformation.tsx 
b/config-ui/src/plugins/jenkins/styled.ts
similarity index 69%
copy from config-ui/src/plugins/gitlab/transformation.tsx
copy to config-ui/src/plugins/jenkins/styled.ts
index 6f9402e83..6da07d584 100644
--- a/config-ui/src/plugins/gitlab/transformation.tsx
+++ b/config-ui/src/plugins/jenkins/styled.ts
@@ -16,19 +16,31 @@
  *
  */
 
-import React from 'react'
+import styled from 'styled-components'
 
-import { CiCd } from './components'
+export const TransformationWrapper = styled.div`
+  h3 {
+    margin: 0 0 8px;
 
-interface Props {
-  transformation: any
-  setTransformation: React.Dispatch<React.SetStateAction<any>>
-}
+    .bp4-tag {
+      margin-left: 6px;
+    }
+  }
 
-export const GitLabTransformation = ({ ...props }: Props) => {
-  return (
-    <>
-      <CiCd {...props} />
-    </>
-  )
-}
+  p {
+    margin: 0 0 8px;
+  }
+
+  .bp4-form-group {
+    display: flex;
+    align-items: center;
+
+    .bp4-label {
+      flex: 0 0 150px;
+    }
+
+    .bp4-form-content {
+      flex: auto;
+    }
+  }
+`
diff --git a/config-ui/src/plugins/jenkins/transformation.tsx 
b/config-ui/src/plugins/jenkins/transformation.tsx
index d4b518de7..fc21a3c23 100644
--- a/config-ui/src/plugins/jenkins/transformation.tsx
+++ b/config-ui/src/plugins/jenkins/transformation.tsx
@@ -19,6 +19,7 @@
 import React from 'react'
 
 import { CiCd } from './components'
+import * as S from './styled'
 
 interface Props {
   transformation: any
@@ -27,8 +28,8 @@ interface Props {
 
 export const JenkinsTransformation = ({ ...props }: Props) => {
   return (
-    <>
+    <S.TransformationWrapper>
       <CiCd {...props} />
-    </>
+    </S.TransformationWrapper>
   )
 }
diff --git a/config-ui/src/plugins/gitlab/transformation.tsx 
b/config-ui/src/plugins/jira/styled.ts
similarity index 69%
copy from config-ui/src/plugins/gitlab/transformation.tsx
copy to config-ui/src/plugins/jira/styled.ts
index 6f9402e83..6da07d584 100644
--- a/config-ui/src/plugins/gitlab/transformation.tsx
+++ b/config-ui/src/plugins/jira/styled.ts
@@ -16,19 +16,31 @@
  *
  */
 
-import React from 'react'
+import styled from 'styled-components'
 
-import { CiCd } from './components'
+export const TransformationWrapper = styled.div`
+  h3 {
+    margin: 0 0 8px;
 
-interface Props {
-  transformation: any
-  setTransformation: React.Dispatch<React.SetStateAction<any>>
-}
+    .bp4-tag {
+      margin-left: 6px;
+    }
+  }
 
-export const GitLabTransformation = ({ ...props }: Props) => {
-  return (
-    <>
-      <CiCd {...props} />
-    </>
-  )
-}
+  p {
+    margin: 0 0 8px;
+  }
+
+  .bp4-form-group {
+    display: flex;
+    align-items: center;
+
+    .bp4-label {
+      flex: 0 0 150px;
+    }
+
+    .bp4-form-content {
+      flex: auto;
+    }
+  }
+`
diff --git a/config-ui/src/plugins/jira/transformation.tsx 
b/config-ui/src/plugins/jira/transformation.tsx
index 81ef46788..28ee7a419 100644
--- a/config-ui/src/plugins/jira/transformation.tsx
+++ b/config-ui/src/plugins/jira/transformation.tsx
@@ -21,6 +21,7 @@ import React from 'react'
 import { Divider } from '@/components'
 
 import { IssueTracking, AdditionalSettings } from './components'
+import * as S from './styled'
 
 interface Props {
   connectionId: ID
@@ -30,10 +31,10 @@ interface Props {
 
 export const JIRATransformation = ({ ...props }: Props) => {
   return (
-    <>
+    <S.TransformationWrapper>
       <IssueTracking {...props} />
       <Divider />
       <AdditionalSettings {...props} />
-    </>
+    </S.TransformationWrapper>
   )
 }
diff --git a/config-ui/src/store/index.ts b/config-ui/src/store/index.ts
index 6199fc0b7..04cd72183 100644
--- a/config-ui/src/store/index.ts
+++ b/config-ui/src/store/index.ts
@@ -16,5 +16,6 @@
  *
  */
 
-export * from './connections'
 export * from './version'
+export * from './connections'
+export * from './transformations'
diff --git a/config-ui/src/pages/index.ts 
b/config-ui/src/store/transformations/api.ts
similarity index 80%
copy from config-ui/src/pages/index.ts
copy to config-ui/src/store/transformations/api.ts
index 405539b29..86d4a44d7 100644
--- a/config-ui/src/pages/index.ts
+++ b/config-ui/src/store/transformations/api.ts
@@ -16,7 +16,9 @@
  *
  */
 
-export * from './project'
-export * from './connection'
-export * from './blueprint'
-export * from './pipeline'
+import request from '@/components/utils/request'
+
+import { Plugins } from '@/plugins'
+
+export const getTransformation = (plugin: Plugins) =>
+  request(`/plugins/${plugin}/transformation_rules`)
diff --git a/config-ui/src/store/transformations/context.tsx 
b/config-ui/src/store/transformations/context.tsx
new file mode 100644
index 000000000..1fe494e1c
--- /dev/null
+++ b/config-ui/src/store/transformations/context.tsx
@@ -0,0 +1,61 @@
+/*
+ * 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, { useContext } from 'react'
+
+import { PageLoading } from '@/components'
+import type { PluginConfigType } from '@/plugins'
+import { Plugins } from '@/plugins'
+
+import type { TransformationItemType } from './types'
+import { useContextValue } from './use-context-value'
+
+const TransformationContext = React.createContext<{
+  plugins: PluginConfigType[]
+  transformations: TransformationItemType[]
+}>({
+  plugins: [],
+  transformations: []
+})
+
+interface Props {
+  children?: React.ReactNode
+}
+
+export const TransformationContextProvider = ({ children }: Props) => {
+  const { loading, plugins, transformations } = useContextValue()
+
+  if (loading) {
+    return <PageLoading />
+  }
+
+  return (
+    <TransformationContext.Provider
+      value={{
+        plugins,
+        transformations
+      }}
+    >
+      {children}
+    </TransformationContext.Provider>
+  )
+}
+
+export const TransformationContextConsumer = TransformationContext.Consumer
+
+export const useTransformation = () => useContext(TransformationContext)
diff --git a/config-ui/src/store/index.ts 
b/config-ui/src/store/transformations/index.ts
similarity index 93%
copy from config-ui/src/store/index.ts
copy to config-ui/src/store/transformations/index.ts
index 6199fc0b7..5f25db57c 100644
--- a/config-ui/src/store/index.ts
+++ b/config-ui/src/store/transformations/index.ts
@@ -16,5 +16,5 @@
  *
  */
 
-export * from './connections'
-export * from './version'
+export * from './types'
+export * from './context'
diff --git a/config-ui/src/pages/index.ts 
b/config-ui/src/store/transformations/types.ts
similarity index 87%
copy from config-ui/src/pages/index.ts
copy to config-ui/src/store/transformations/types.ts
index 405539b29..62b600c89 100644
--- a/config-ui/src/pages/index.ts
+++ b/config-ui/src/store/transformations/types.ts
@@ -16,7 +16,10 @@
  *
  */
 
-export * from './project'
-export * from './connection'
-export * from './blueprint'
-export * from './pipeline'
+import { Plugins } from '@/plugins'
+
+export type TransformationItemType = {
+  id: ID
+  name: string
+  plugin: Plugins
+}
diff --git a/config-ui/src/store/transformations/use-context-value.ts 
b/config-ui/src/store/transformations/use-context-value.ts
new file mode 100644
index 000000000..5c4411dbe
--- /dev/null
+++ b/config-ui/src/store/transformations/use-context-value.ts
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { useState, useEffect, useCallback, useMemo } from 'react'
+
+import { PluginConfig, PluginType, Plugins } from '@/plugins'
+
+import type { TransformationItemType } from './types'
+import * as API from './api'
+
+export const useContextValue = () => {
+  const [loading, setLoading] = useState(false)
+  const [transformations, setTransformations] = useState<
+    TransformationItemType[]
+  >([])
+
+  const allConnections = useMemo(
+    () => PluginConfig.filter((p) => p.type === PluginType.Connection),
+    []
+  )
+
+  const getTransformation = async (plugin: Plugins) => {
+    try {
+      return await API.getTransformation(plugin)
+    } catch {
+      return []
+    }
+  }
+
+  const handleRefresh = useCallback(async () => {
+    setLoading(true)
+
+    const res = await Promise.all(
+      allConnections.map((cs) => getTransformation(cs.plugin))
+    )
+
+    const resWithPlugin = res.map((ts, i) =>
+      ts.map((it: any) => {
+        const { plugin } = allConnections[i]
+
+        return {
+          ...it,
+          plugin
+        }
+      })
+    )
+
+    setTransformations(resWithPlugin.flat())
+    setLoading(false)
+  }, [allConnections])
+
+  useEffect(() => {
+    handleRefresh()
+  }, [])
+
+  return useMemo(
+    () => ({
+      loading,
+      plugins: allConnections,
+      transformations
+    }),
+    [loading, allConnections, transformations]
+  )
+}


Reply via email to