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 2022/11/15 12:36:23 UTC
[incubator-devlake] branch main updated: feat(config-ui): gitlab project chooses to support the miller-columns component (#3717)
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 cdc7ef08a feat(config-ui): gitlab project chooses to support the miller-columns component (#3717)
cdc7ef08a is described below
commit cdc7ef08a9064a8353ec932ad4869f31432549f2
Author: 青湛 <0x...@gmail.com>
AuthorDate: Tue Nov 15 20:36:19 2022 +0800
feat(config-ui): gitlab project chooses to support the miller-columns component (#3717)
* refactor(config-ui): remove content about the miller-columns test
* feat(config-ui): optimize some content about miller-columns
* feat(config-ui): gitlab project chooses to support the miller-columns component
* refactor(config-ui): remove choose all on the miller-columns
* feat(config-ui): optimize component miller-columns
* refactor(config-ui): organize directory about gitlab component
* feat(config-ui): added component project selector about gitlab
* refactor(config-ui): remove content about old gitlab project selector and useGitlab
* feat(config-ui): control gitlab miller columns show on edit blueprint
* refactor(config-ui): remove content related to gitlab api proxy config
* feat(config-ui): optimize checkbox click area
* fix(config-ui): failed to get user's project correctly
* refactor(config-ui): optimize the linkage between miller-columns and project-selector components of gitlab
* feat(config-ui): show parent active on miller-column
* refactor(config-ui): assemble the column component
* feat(config-ui): expand the number of gitlab requests
---
config-ui/src/App.js | 12 --
.../blueprints/GitlabProjectsSelector.jsx | 155 -----------------
.../blueprints/create-workflow/DataScopes.jsx | 78 +++++++--
.../checkbox/types.ts => gitlab/config.ts} | 7 +-
.../components/{miller-columns => gitlab}/index.ts | 2 +-
.../src/components/gitlab/miller-columns/index.tsx | 108 ++++++++++++
.../index.ts => gitlab/miller-columns/styled.ts} | 7 +-
.../miller-columns/use-gitlab-miller-columns.ts | 94 +++++++++++
.../components/gitlab/project-selector/index.tsx | 113 +++++++++++++
.../index.ts => gitlab/project-selector/styled.ts} | 9 +-
.../use-gitlab-project-selector.ts | 98 +++++++++++
config-ui/src/components/gitlab/request.ts | 63 +++++++
.../components/checkbox/checkbox.tsx | 24 ++-
.../miller-columns/components/checkbox/styled.ts | 16 ++
.../miller-columns/components/checkbox/types.ts | 3 +-
.../{item/item-all.tsx => column/column.tsx} | 28 ++-
.../components/{ => column}/index.ts | 3 +-
.../{ => components/column}/styled.ts | 34 ++--
.../components/miller-columns/components/index.ts | 1 +
.../miller-columns/components/item/index.ts | 1 -
.../miller-columns/components/item/item.tsx | 23 ++-
.../miller-columns/components/item/styled.ts | 34 ++--
.../src/components/miller-columns/hooks/index.ts | 2 +-
.../miller-columns/hooks/use-item-map.ts | 34 ++--
.../miller-columns/hooks/use-load-items.ts | 100 +++++++++++
.../miller-columns/hooks/use-miller-columns.ts | 187 ++++++++-------------
.../components/miller-columns/hooks/use-test.ts | 116 -------------
config-ui/src/components/miller-columns/index.ts | 4 +-
.../components/miller-columns/miller-columns.tsx | 74 ++++----
config-ui/src/components/miller-columns/styled.ts | 25 +--
config-ui/src/components/miller-columns/types.ts | 21 ++-
config-ui/src/config/gitlabApiProxy.js | 23 ---
config-ui/src/hooks/useGitlab.jsx | 100 -----------
config-ui/src/models/GitlabProject.js | 1 +
.../src/pages/blueprints/blueprint-settings.jsx | 25 +--
.../src/pages/blueprints/create-blueprint.jsx | 23 ---
config-ui/tsconfig.json | 4 +
37 files changed, 896 insertions(+), 756 deletions(-)
diff --git a/config-ui/src/App.js b/config-ui/src/App.js
index c4729c450..1fdf94476 100644
--- a/config-ui/src/App.js
+++ b/config-ui/src/App.js
@@ -42,7 +42,6 @@ import BlueprintDetail from '@/pages/blueprints/blueprint-detail'
import BlueprintSettings from '@/pages/blueprints/blueprint-settings'
import { IncomingWebhook as IncomingWebhookConnection } from '@/pages/connections/incoming-webhook'
import MigrationAlertDialog from '@/components/MigrationAlertDialog'
-import { MillerColumns, useTest } from '@/components/miller-columns'
function App(props) {
const {
@@ -56,8 +55,6 @@ function App(props) {
handleMigrationDialogClose
} = useDatabaseMigrations()
- const { items, ids, setIds } = useTest()
-
return (
<Router>
<Route exact path='/'>
@@ -108,15 +105,6 @@ function App(props) {
<Route exact path='/connections/incoming-webhook'>
<IncomingWebhookConnection />
</Route>
- <Route exact path='/miller-columns'>
- <MillerColumns
- height={624}
- firstColumnTitle='Organizations/Owners'
- items={items}
- selectedItemIds={ids}
- onSelectedItemIds={(ids) => setIds(ids)}
- />
- </Route>
<Route exact path='/offline'>
<Offline />
</Route>
diff --git a/config-ui/src/components/blueprints/GitlabProjectsSelector.jsx b/config-ui/src/components/blueprints/GitlabProjectsSelector.jsx
deleted file mode 100644
index 5b17996c5..000000000
--- a/config-ui/src/components/blueprints/GitlabProjectsSelector.jsx
+++ /dev/null
@@ -1,155 +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, { useEffect, useState } from 'react'
-import {
- Checkbox,
- Intent,
- MenuItem,
- Position,
- Tooltip
-} from '@blueprintjs/core'
-import { MultiSelect } from '@blueprintjs/select'
-import GitlabProject from '@/models/GitlabProject'
-
-const GitlabProjectsSelector = (props) => {
- const {
- onFetch = () => [],
- isFetching = false,
- configuredConnection,
- placeholder = 'Select Projects',
- items = [],
- selectedItems = [],
- activeItem = null,
- disabled = false,
- isLoading = false,
- isSaving = false,
- onItemSelect = () => {},
- onRemove = () => {},
- onClear = () => {},
- itemRenderer = (item, { handleClick, modifiers }) => (
- <MenuItem
- active={modifiers.active}
- disabled={selectedItems.find((i) => i?.id === item?.id)}
- key={item.value}
- onClick={handleClick}
- text={
- selectedItems.find((i) => i?.id === item?.id) ? (
- <>
- <input type='checkbox' checked readOnly /> {item?.title}
- </>
- ) : (
- <span style={{ fontWeight: 700 }}>
- <input type='checkbox' readOnly /> {item?.title}
- </span>
- )
- }
- style={{
- marginBottom: '2px',
- fontWeight: items.includes(item) ? 700 : 'normal'
- }}
- />
- ),
- // eslint-disable-next-line max-len
- tagRenderer = (item) => (
- <Tooltip
- intent={Intent.PRIMARY}
- content={item?.title}
- position={Position.TOP}
- >
- {item.shortTitle || item.title}
- </Tooltip>
- )
- } = props
-
- const [query, setQuery] = useState('')
- const [onlyQueryMemberRepo, setOnlyQueryMemberRepo] = useState(true)
-
- useEffect(() => {
- // prevent request too frequently
- const timer = setTimeout(() => {
- onFetch(query, onlyQueryMemberRepo)
- }, 200)
- return () => clearTimeout(timer)
- }, [onFetch, query, onlyQueryMemberRepo])
-
- return (
- <div
- className='gitlab-projects-multiselect'
- style={{ display: 'flex', marginBottom: '10px' }}
- >
- <div
- className='gitlab-projects-multiselect-selector'
- style={{ minWidth: '200px', width: '100%' }}
- >
- <MultiSelect
- disabled={disabled || isSaving || isLoading}
- // openOnKeyDown={true}
- resetOnSelect={true}
- placeholder={placeholder}
- popoverProps={{ usePortal: false, minimal: true }}
- className='multiselector-projects'
- inline={true}
- fill={true}
- items={items}
- selectedItems={selectedItems}
- activeItem={activeItem}
- onQueryChange={(query) => setQuery(query)}
- itemRenderer={itemRenderer}
- tagRenderer={tagRenderer}
- tagInputProps={{
- tagProps: {
- intent: Intent.PRIMARY,
- minimal: true
- }
- }}
- noResults={
- (query.length <= 2 && (
- <MenuItem
- disabled={true}
- text='Please type more than 2 characters to search.'
- />
- )) ||
- (isFetching && <MenuItem disabled={true} text='Fetching...' />) || (
- <MenuItem disabled={true} text='No Projects Available.' />
- )
- }
- onRemove={(item) => {
- onRemove(selectedItems.filter((t) => t?.id !== item.id))
- }}
- onItemSelect={(item) => {
- onItemSelect(
- !selectedItems.includes(item)
- ? [...selectedItems, new GitlabProject(item)]
- : selectedItems
- )
- }}
- style={{ borderRight: 0 }}
- />
-
- <Checkbox
- label='Only search my repositories'
- checked={onlyQueryMemberRepo}
- onChange={(e) => setOnlyQueryMemberRepo(!onlyQueryMemberRepo)}
- style={{ margin: '10px 0 0 6px' }}
- />
- </div>
- </div>
- )
-}
-
-export default GitlabProjectsSelector
diff --git a/config-ui/src/components/blueprints/create-workflow/DataScopes.jsx b/config-ui/src/components/blueprints/create-workflow/DataScopes.jsx
index 3f924e110..9b3a3593c 100644
--- a/config-ui/src/components/blueprints/create-workflow/DataScopes.jsx
+++ b/config-ui/src/components/blueprints/create-workflow/DataScopes.jsx
@@ -30,7 +30,8 @@ import ConnectionTabs from '@/components/blueprints/ConnectionTabs'
import BoardsSelector from '@/components/blueprints/BoardsSelector'
import DataDomainsSelector from '@/components/blueprints/DataDomainsSelector'
import NoData from '@/components/NoData'
-import GitlabProjectsSelector from '@/components/blueprints/GitlabProjectsSelector'
+import { GitLabMillerColumns, GitLabProjectSelector } from '@/components/gitlab'
+import GitlabProject from '@/models/GitlabProject'
import GitHubProject from '@/models/GithubProject'
import JenkinsJobsSelector from '@/components/blueprints/JenkinsJobsSelector'
@@ -40,9 +41,6 @@ const DataScopes = (props) => {
activeConnectionTab,
blueprintConnections = [],
jiraBoards = [],
- fetchGitlabProjects = () => [],
- isFetchingGitlab = false,
- gitlabProjects = [],
fetchJenkinsJobs = () => [],
isFetchingJenkins = false,
jenkinsJobs = [],
@@ -211,18 +209,66 @@ const DataScopes = (props) => {
) && (
<>
<h4>Projects *</h4>
- <p>Select the project you would like to sync.</p>
- <GitlabProjectsSelector
- onFetch={fetchGitlabProjects}
- isFetching={isFetchingGitlab}
- items={gitlabProjects}
- selectedItems={selectedScopeEntities}
- onItemSelect={setScopeEntities}
- onClear={setScopeEntities}
- onRemove={setScopeEntities}
- disabled={isSaving}
- configuredConnection={configuredConnection}
- isLoading={isFetching}
+ {!!activeStep && (
+ <>
+ <p>Select the project you would like to sync.</p>
+ <GitLabMillerColumns
+ connectionId={configuredConnection.connectionId}
+ disabledItemIds={selectedScopeEntities
+ .filter((it) => it.type !== 'miller-columns')
+ .map((it) => it.id)}
+ onChangeItems={(items) =>
+ setScopeEntities([
+ ...scopeEntitiesGroup[
+ configuredConnection.id
+ ].filter((it) => it.type !== 'miller-columns'),
+ ...items.map(
+ (it) =>
+ new GitlabProject({
+ ...it,
+ type: 'miller-columns'
+ })
+ )
+ ])
+ }
+ />
+ </>
+ )}
+ <div style={{ margin: '16px 0 8px' }}>
+ Add repositories outside of your projects
+ </div>
+ <p>
+ Enter the repositories using the format “owner/repo” and
+ separate multiple repos with a comma.
+ </p>
+ <GitLabProjectSelector
+ connectionId={configuredConnection.connectionId}
+ disabledItemIds={selectedScopeEntities
+ .filter((it) => it.type !== 'project-selector')
+ .map((it) => it.id)}
+ selectedItems={selectedScopeEntities
+ .filter((it) => it.type === 'project-selector')
+ .map((it) => ({
+ id: it.id,
+ key: it.id,
+ title: it.title,
+ shortTitle: it.shortTitle,
+ value: it.id
+ }))}
+ onChangeItems={(items) =>
+ setScopeEntities([
+ ...scopeEntitiesGroup[
+ configuredConnection.id
+ ].filter((it) => it.type !== 'project-selector'),
+ ...items.map(
+ (it) =>
+ new GitlabProject({
+ ...it,
+ type: 'project-selector'
+ })
+ )
+ ])
+ }
/>
</>
)}
diff --git a/config-ui/src/components/miller-columns/components/checkbox/types.ts b/config-ui/src/components/gitlab/config.ts
similarity index 86%
copy from config-ui/src/components/miller-columns/components/checkbox/types.ts
copy to config-ui/src/components/gitlab/config.ts
index eb33ae2df..549f4a16d 100644
--- a/config-ui/src/components/miller-columns/components/checkbox/types.ts
+++ b/config-ui/src/components/gitlab/config.ts
@@ -16,8 +16,5 @@
*
*/
-export enum CheckStatus {
- nochecked = 'nochecked',
- checked = 'checked',
- indeterminate = 'indeterminate'
-}
+export const getGitLabProxyApiPrefix = (connectionId: string) =>
+ `/plugins/gitlab/connections/${connectionId}/proxy/rest`
diff --git a/config-ui/src/components/miller-columns/index.ts b/config-ui/src/components/gitlab/index.ts
similarity index 95%
copy from config-ui/src/components/miller-columns/index.ts
copy to config-ui/src/components/gitlab/index.ts
index 07cc2c3f3..0b86f1dd2 100644
--- a/config-ui/src/components/miller-columns/index.ts
+++ b/config-ui/src/components/gitlab/index.ts
@@ -17,4 +17,4 @@
*/
export * from './miller-columns'
-export * from './hooks/use-test'
+export * from './project-selector'
diff --git a/config-ui/src/components/gitlab/miller-columns/index.tsx b/config-ui/src/components/gitlab/miller-columns/index.tsx
new file mode 100644
index 000000000..648d4602a
--- /dev/null
+++ b/config-ui/src/components/gitlab/miller-columns/index.tsx
@@ -0,0 +1,108 @@
+/*
+ * 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, useState } from 'react'
+
+import type { ColumnType, ItemType } from '@/components/miller-columns'
+import {
+ MillerColumns,
+ ItemStatusEnum,
+ ItemTypeEnum
+} from '@/components/miller-columns'
+
+import {
+ useGitLabMillerColumns,
+ UseGitLabMillerColumnsProps
+} from './use-gitlab-miller-columns'
+import * as S from './styled'
+
+interface Props extends UseGitLabMillerColumnsProps {
+ disabledItemIds?: Array<number>
+ onChangeItems: (
+ items: Array<Pick<ItemType, 'id' | 'title'> & { shortTitle: string }>
+ ) => void
+}
+
+export const GitLabMillerColumns = ({
+ connectionId,
+ disabledItemIds,
+ onChangeItems
+}: Props) => {
+ const [seletedIds, setSelectedIds] = useState<Array<ItemType['id']>>([])
+
+ const { items, itemTree, onExpandItem } = useGitLabMillerColumns<{
+ nameWithNameSpace?: string
+ }>({
+ connectionId
+ })
+
+ useEffect(() => {
+ const curItems = seletedIds
+ .filter((id) => itemTree[id].type === ItemTypeEnum.LEAF)
+ .map((id) => ({
+ id,
+ title: itemTree[id].nameWithNameSpace ?? '',
+ shortTitle: itemTree[id].title
+ }))
+
+ onChangeItems(curItems)
+ }, [seletedIds])
+
+ const renderColumnBottom = ({
+ isLoading,
+ isEmpty
+ }: {
+ isLoading: boolean
+ isEmpty: boolean
+ }) => {
+ switch (true) {
+ case isLoading:
+ return <S.Placeholder>Loading...</S.Placeholder>
+ case isEmpty:
+ return <S.Placeholder>No Data.</S.Placeholder>
+ }
+ }
+
+ return (
+ <MillerColumns
+ height={300}
+ firstColumnTitle='Subgroups/Projects'
+ items={items}
+ disabledItemIds={disabledItemIds}
+ selectedItemIds={seletedIds}
+ onSelectedItemIds={setSelectedIds}
+ onExpandItem={onExpandItem}
+ renderColumnBottom={(col: ColumnType) => {
+ if (!col.parentId) {
+ return renderColumnBottom({
+ isLoading: !itemTree.root,
+ isEmpty: !itemTree.root || !itemTree.root.items.length
+ })
+ } else {
+ return renderColumnBottom({
+ isLoading:
+ !itemTree[col.parentId] ||
+ itemTree[col.parentId].status === ItemStatusEnum.PENDING,
+ isEmpty:
+ !itemTree[col.parentId] || !itemTree[col.parentId].items.length
+ })
+ }
+ }}
+ />
+ )
+}
diff --git a/config-ui/src/components/miller-columns/components/item/index.ts b/config-ui/src/components/gitlab/miller-columns/styled.ts
similarity index 88%
copy from config-ui/src/components/miller-columns/components/item/index.ts
copy to config-ui/src/components/gitlab/miller-columns/styled.ts
index d8175878e..6b7cbc9e7 100644
--- a/config-ui/src/components/miller-columns/components/item/index.ts
+++ b/config-ui/src/components/gitlab/miller-columns/styled.ts
@@ -16,5 +16,8 @@
*
*/
-export * from './item'
-export * from './item-all'
+import styled from '@emotion/styled'
+
+export const Placeholder = styled.div`
+ padding: 4px 12px;
+`
diff --git a/config-ui/src/components/gitlab/miller-columns/use-gitlab-miller-columns.ts b/config-ui/src/components/gitlab/miller-columns/use-gitlab-miller-columns.ts
new file mode 100644
index 000000000..1874f73c1
--- /dev/null
+++ b/config-ui/src/components/gitlab/miller-columns/use-gitlab-miller-columns.ts
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { useMemo, useCallback } from 'react'
+
+import type { ItemType } from '@/components/miller-columns'
+import { useLoadItems, ItemTypeEnum } from '@/components/miller-columns'
+
+import request from '../request'
+import { getGitLabProxyApiPrefix } from '../config'
+
+export interface UseGitLabMillerColumnsProps {
+ connectionId: string
+}
+
+export const useGitLabMillerColumns = <T>({
+ connectionId
+}: UseGitLabMillerColumnsProps) => {
+ const prefix = useMemo(
+ () => getGitLabProxyApiPrefix(connectionId),
+ [connectionId]
+ )
+
+ const upadateGroups = (arr: any): Array<ItemType> =>
+ arr.map((it: any) => ({
+ id: it.id,
+ title: it.name,
+ type: ItemTypeEnum.BRANCH,
+ items: []
+ }))
+
+ const updateProjects = (arr: any): Array<ItemType> =>
+ arr.map((it: any) => ({
+ id: it.id,
+ title: it.name,
+ type: ItemTypeEnum.LEAF,
+ items: [],
+ nameWithNameSpace: it.name_with_namespace
+ }))
+
+ const getInitItems = useCallback(async () => {
+ const user = await request(`${prefix}/user`)
+ const [groups, projects] = await Promise.all([
+ request(`${prefix}/groups`, {
+ data: { top_level_only: 1, per_page: 100 }
+ }),
+ request(`${prefix}/users/${user.id}/projects`, {
+ data: { per_page: 100 }
+ })
+ ])
+ return [...upadateGroups(groups), ...updateProjects(projects)]
+ }, [prefix])
+
+ const loadMoreItems = useCallback(
+ async (item: ItemType) => {
+ const [groups, projects] = await Promise.all([
+ request(`${prefix}/groups/${item.id}/subgroups`, {
+ data: { per_page: 100 }
+ }),
+ request(`${prefix}/groups/${item.id}/projects`, {
+ data: { per_page: 100 }
+ })
+ ])
+ return [...upadateGroups(groups), ...updateProjects(projects)]
+ },
+ [prefix]
+ )
+
+ const { items, itemTree, loadItems } = useLoadItems<T>({
+ getInitItems,
+ loadMoreItems
+ })
+
+ return {
+ items,
+ itemTree,
+ onExpandItem: loadItems
+ }
+}
diff --git a/config-ui/src/components/gitlab/project-selector/index.tsx b/config-ui/src/components/gitlab/project-selector/index.tsx
new file mode 100644
index 000000000..62ca6243c
--- /dev/null
+++ b/config-ui/src/components/gitlab/project-selector/index.tsx
@@ -0,0 +1,113 @@
+/*
+ * 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 { MultiSelect } from '@blueprintjs/select'
+import { Checkbox, MenuItem, Intent } from '@blueprintjs/core'
+
+import type { ItemType } from './use-gitlab-project-selector'
+import {
+ useGitLabProjectSelector,
+ UseGitLabProjectSelectorProps
+} from './use-gitlab-project-selector'
+import * as S from './styled'
+
+interface Props extends UseGitLabProjectSelectorProps {
+ disabledItemIds?: Array<ItemType['id']>
+}
+
+export const GitLabProjectSelector = ({
+ connectionId,
+ disabledItemIds,
+ selectedItems,
+ onChangeItems
+}: Props) => {
+ const {
+ loading,
+ items,
+ search,
+ membership,
+ onSearch,
+ onChangeMembership,
+ onSelect,
+ onRemove
+ } = useGitLabProjectSelector({
+ connectionId,
+ selectedItems,
+ onChangeItems
+ })
+
+ const tagRenderer = (item: any) => {
+ return <span>{item.shortTitle || item.title}</span>
+ }
+
+ const itemRenderer = (item: ItemType, { handleClick }: any) => {
+ const selected = !![
+ ...selectedItems.map((it) => it.id),
+ ...(disabledItemIds ?? [])
+ ].find((id) => id === item.id)
+
+ return (
+ <MenuItem
+ key={item.key}
+ text={
+ <Checkbox label={item.title} checked={selected} disabled={selected} />
+ }
+ disabled={selected}
+ onClick={handleClick}
+ />
+ )
+ }
+
+ return (
+ <S.Container>
+ <MultiSelect
+ className='selector'
+ placeholder='Select Projects'
+ popoverProps={{ usePortal: false, minimal: true, isOpen: !!search }}
+ resetOnSelect
+ fill
+ items={items}
+ selectedItems={selectedItems}
+ tagInputProps={{
+ tagProps: {
+ intent: Intent.PRIMARY,
+ minimal: true
+ }
+ }}
+ noResults={
+ <MenuItem
+ disabled={true}
+ text={loading ? 'Fetching...' : 'No Projects Available.'}
+ />
+ }
+ tagRenderer={tagRenderer}
+ itemRenderer={itemRenderer}
+ onQueryChange={onSearch}
+ onItemSelect={onSelect}
+ onRemove={onRemove}
+ />
+ <Checkbox
+ className='checkbox'
+ label='Only search my repositories'
+ checked={membership}
+ onChange={onChangeMembership}
+ />
+ </S.Container>
+ )
+}
diff --git a/config-ui/src/components/miller-columns/components/item/index.ts b/config-ui/src/components/gitlab/project-selector/styled.ts
similarity index 87%
copy from config-ui/src/components/miller-columns/components/item/index.ts
copy to config-ui/src/components/gitlab/project-selector/styled.ts
index d8175878e..164822357 100644
--- a/config-ui/src/components/miller-columns/components/item/index.ts
+++ b/config-ui/src/components/gitlab/project-selector/styled.ts
@@ -16,5 +16,10 @@
*
*/
-export * from './item'
-export * from './item-all'
+import styled from '@emotion/styled'
+
+export const Container = styled.div`
+ .checkbox {
+ margin: 8px 0 0;
+ }
+`
diff --git a/config-ui/src/components/gitlab/project-selector/use-gitlab-project-selector.ts b/config-ui/src/components/gitlab/project-selector/use-gitlab-project-selector.ts
new file mode 100644
index 000000000..66491d534
--- /dev/null
+++ b/config-ui/src/components/gitlab/project-selector/use-gitlab-project-selector.ts
@@ -0,0 +1,98 @@
+/*
+ * 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 request from '../request'
+import { getGitLabProxyApiPrefix } from '../config'
+
+export type ItemType = {
+ id: number
+ key: number
+ title: string
+ shortTitle: string
+}
+
+export interface UseGitLabProjectSelectorProps {
+ connectionId: string
+ selectedItems: Array<ItemType>
+ onChangeItems: (items: Array<ItemType>) => void
+}
+
+export const useGitLabProjectSelector = ({
+ connectionId,
+ selectedItems,
+ onChangeItems
+}: UseGitLabProjectSelectorProps) => {
+ const [loading, setLoading] = useState(false)
+ const [items, setItems] = useState([])
+ const [search, setSearch] = useState('')
+ const [membership, setMembership] = useState(true)
+
+ const prefix = useMemo(
+ () => getGitLabProxyApiPrefix(connectionId),
+ [connectionId]
+ )
+
+ useEffect(() => {
+ if (!search) return
+ setItems([])
+ setLoading(true)
+
+ const apiPath = `${prefix}/projects`
+
+ const timer = setTimeout(async () => {
+ const res = await request(apiPath, { data: { search, membership } })
+ setItems(
+ res.map((it: any) => ({
+ id: it.id,
+ key: it.id,
+ title: it.name_with_namespace,
+ shortTitle: it.name
+ }))
+ )
+ setLoading(false)
+ }, 1000)
+
+ return () => clearTimeout(timer)
+ }, [prefix, search, membership])
+
+ return useMemo(
+ () => ({
+ loading,
+ items,
+ search,
+ membership,
+ onSearch(s: string) {
+ setSearch(s)
+ },
+ onChangeMembership(e: React.ChangeEvent<HTMLInputElement>) {
+ setMembership(e.target.checked)
+ },
+ onSelect(item: ItemType) {
+ const newItems = [...selectedItems, item]
+ onChangeItems(newItems)
+ },
+ onRemove(item: ItemType) {
+ const newItems = selectedItems.filter((it) => item.id !== it.id)
+ onChangeItems(newItems)
+ }
+ }),
+ [loading, items, search, membership]
+ )
+}
diff --git a/config-ui/src/components/gitlab/request.ts b/config-ui/src/components/gitlab/request.ts
new file mode 100644
index 000000000..c47cb2d7f
--- /dev/null
+++ b/config-ui/src/components/gitlab/request.ts
@@ -0,0 +1,63 @@
+/*
+ * 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 type { AxiosRequestConfig } from 'axios'
+import axios from 'axios'
+
+const instance = axios.create({
+ baseURL: '/api'
+})
+
+export type ReuqestConfig = {
+ method?: AxiosRequestConfig['method']
+ data?: unknown
+ timeout?: number
+ signal?: AbortSignal
+ headers?: Record<string, string>
+}
+
+const request = (path: string, config?: ReuqestConfig) => {
+ const { method = 'GET', data, timeout, headers, signal } = config || {}
+
+ const cancelTokenSource = axios.CancelToken.source()
+ const params: any = {
+ url: path,
+ method,
+ timeout,
+ headers,
+ cancelToken: cancelTokenSource?.token
+ }
+
+ if (method === 'GET') {
+ params.params = data
+ } else {
+ params.data = data
+ }
+
+ const promise = instance.request(params).then((resp) => resp.data)
+
+ if (signal) {
+ signal.addEventListener('abort', () => {
+ cancelTokenSource?.cancel()
+ })
+ }
+
+ return promise
+}
+
+export default request
diff --git a/config-ui/src/components/miller-columns/components/checkbox/checkbox.tsx b/config-ui/src/components/miller-columns/components/checkbox/checkbox.tsx
index a3394e587..a8b7b6649 100644
--- a/config-ui/src/components/miller-columns/components/checkbox/checkbox.tsx
+++ b/config-ui/src/components/miller-columns/components/checkbox/checkbox.tsx
@@ -23,20 +23,32 @@ import { CheckStatus } from './types'
import * as S from './styled'
interface Props {
- status?: CheckStatus
+ status?: CheckStatus | Array<CheckStatus>
children?: React.ReactNode
- onClick?: (e: React.MouseEvent<HTMLDivElement>) => void
+ onClick?: (e: React.MouseEvent<HTMLLabelElement>) => void
}
export const Checkbox = ({ children, status, onClick }: Props) => {
const checkboxCls = classNames('checkbox', {
- 'checkbox-checked': status === CheckStatus.checked,
- 'checkbox-indeterminate': status === CheckStatus.indeterminate
+ 'checkbox-checked':
+ status === CheckStatus.checked ||
+ (Array.isArray(status) && status.includes(CheckStatus.checked)),
+ 'checkbox-indeterminate':
+ status === CheckStatus.indeterminate ||
+ (Array.isArray(status) && status.includes(CheckStatus.indeterminate)),
+ 'checkbox-disabled':
+ status === CheckStatus.disabled ||
+ (Array.isArray(status) && status?.includes(CheckStatus.disabled))
})
+ const handleClick = (e: React.MouseEvent<HTMLLabelElement>) => {
+ if (status === CheckStatus.disabled) return
+ onClick?.(e)
+ }
+
return (
- <S.Wrapper>
- <span className={checkboxCls} onClick={onClick}>
+ <S.Wrapper onClick={handleClick}>
+ <span className={checkboxCls}>
<span className='checkbox-inner'></span>
</span>
{children && <span className='text'>{children}</span>}
diff --git a/config-ui/src/components/miller-columns/components/checkbox/styled.ts b/config-ui/src/components/miller-columns/components/checkbox/styled.ts
index 1eda37188..4d2d2c451 100644
--- a/config-ui/src/components/miller-columns/components/checkbox/styled.ts
+++ b/config-ui/src/components/miller-columns/components/checkbox/styled.ts
@@ -59,6 +59,22 @@ export const Wrapper = styled.label`
}
}
+ &.checkbox-disabled {
+ cursor: not-allowed;
+
+ .checkbox-inner {
+ background-color: #f5f5f5;
+ border-color: #d9d9d9;
+
+ &::after {
+ background-color: #cccccc;
+ border-color: #f5f5f5;
+ border-collapse: separate;
+ animation-name: none;
+ }
+ }
+ }
+
.checkbox-input {
position: absolute;
z-index: 1;
diff --git a/config-ui/src/components/miller-columns/components/checkbox/types.ts b/config-ui/src/components/miller-columns/components/checkbox/types.ts
index eb33ae2df..b2ab04602 100644
--- a/config-ui/src/components/miller-columns/components/checkbox/types.ts
+++ b/config-ui/src/components/miller-columns/components/checkbox/types.ts
@@ -19,5 +19,6 @@
export enum CheckStatus {
nochecked = 'nochecked',
checked = 'checked',
- indeterminate = 'indeterminate'
+ indeterminate = 'indeterminate',
+ disabled = 'disabled'
}
diff --git a/config-ui/src/components/miller-columns/components/item/item-all.tsx b/config-ui/src/components/miller-columns/components/column/column.tsx
similarity index 62%
rename from config-ui/src/components/miller-columns/components/item/item-all.tsx
rename to config-ui/src/components/miller-columns/components/column/column.tsx
index a0dc0cb95..967cf005d 100644
--- a/config-ui/src/components/miller-columns/components/item/item-all.tsx
+++ b/config-ui/src/components/miller-columns/components/column/column.tsx
@@ -18,28 +18,24 @@
import React from 'react'
-import type { ColumnType } from '../../types'
-
-import { Checkbox, CheckStatus } from '../checkbox'
+import type { ItemType } from '../../types'
import * as S from './styled'
interface Props {
- column: ColumnType
- checkStatus?: CheckStatus
- onSelectAllItem?: (column: ColumnType) => void
+ items: Array<ItemType>
+ renderItem: (item: ItemType) => React.ReactNode
+ height?: number
+ title?: string | React.ReactNode
+ bottom?: React.ReactNode
}
-export const ItemAll = ({ column, checkStatus, onSelectAllItem }: Props) => {
- const handleCheckboxClick = (e: React.MouseEvent<HTMLDivElement>) => {
- e.stopPropagation()
- onSelectAllItem?.(column)
- }
-
+export const Column = ({ items, renderItem, height, title, bottom }: Props) => {
return (
- <S.Wrapper selected={false}>
- <Checkbox status={checkStatus} onClick={handleCheckboxClick} />
- <span>All</span>
- </S.Wrapper>
+ <S.Container height={height}>
+ {title && <div className='title'>{title}</div>}
+ {items.map((it) => renderItem(it))}
+ {bottom}
+ </S.Container>
)
}
diff --git a/config-ui/src/components/miller-columns/components/index.ts b/config-ui/src/components/miller-columns/components/column/index.ts
similarity index 94%
copy from config-ui/src/components/miller-columns/components/index.ts
copy to config-ui/src/components/miller-columns/components/column/index.ts
index 25c3b9507..c9f2a97c2 100644
--- a/config-ui/src/components/miller-columns/components/index.ts
+++ b/config-ui/src/components/miller-columns/components/column/index.ts
@@ -16,5 +16,4 @@
*
*/
-export * from './checkbox'
-export * from './item'
+export * from './column'
diff --git a/config-ui/src/components/miller-columns/styled.ts b/config-ui/src/components/miller-columns/components/column/styled.ts
similarity index 72%
copy from config-ui/src/components/miller-columns/styled.ts
copy to config-ui/src/components/miller-columns/components/column/styled.ts
index f22fb81d2..b91d76491 100644
--- a/config-ui/src/components/miller-columns/styled.ts
+++ b/config-ui/src/components/miller-columns/components/column/styled.ts
@@ -19,28 +19,22 @@
import styled from '@emotion/styled'
export const Container = styled.div<{ height?: number }>`
- display: flex;
- width: 100%;
+ flex: 0 0 33.33%;
+ margin: 0;
+ padding: 0;
+ width: 33.33%;
${({ height }) => `height: ${height}px;`}
- overflow-x: auto;
+ list-style: none;
+ border-left: 1px solid #dbe4fd;
+ overflow-y: auto;
- .items {
- flex: 1;
- margin: 0;
- padding: 0;
- list-style: none;
- border: 1px solid #dbe4fd;
- border-right: none;
- border-radius: 4px;
-
- &:last-child {
- border-right: 1px solid #dbe4fd;
- }
+ &:first-child {
+ border-left: none;
+ }
- & > .title {
- padding: 4px 12px;
- font-weight: 700;
- color: #292b3f;
- }
+ & > .title {
+ padding: 4px 12px;
+ font-weight: 700;
+ color: #292b3f;
}
`
diff --git a/config-ui/src/components/miller-columns/components/index.ts b/config-ui/src/components/miller-columns/components/index.ts
index 25c3b9507..806888c42 100644
--- a/config-ui/src/components/miller-columns/components/index.ts
+++ b/config-ui/src/components/miller-columns/components/index.ts
@@ -18,3 +18,4 @@
export * from './checkbox'
export * from './item'
+export * from './column'
diff --git a/config-ui/src/components/miller-columns/components/item/index.ts b/config-ui/src/components/miller-columns/components/item/index.ts
index d8175878e..0c230080b 100644
--- a/config-ui/src/components/miller-columns/components/item/index.ts
+++ b/config-ui/src/components/miller-columns/components/item/index.ts
@@ -17,4 +17,3 @@
*/
export * from './item'
-export * from './item-all'
diff --git a/config-ui/src/components/miller-columns/components/item/item.tsx b/config-ui/src/components/miller-columns/components/item/item.tsx
index ca71d17cc..172e39534 100644
--- a/config-ui/src/components/miller-columns/components/item/item.tsx
+++ b/config-ui/src/components/miller-columns/components/item/item.tsx
@@ -18,7 +18,7 @@
import React from 'react'
-import { ItemType, RowStatus } from '../../types'
+import { ItemType, ItemTypeEnum, RowStatus } from '../../types'
import { Checkbox, CheckStatus } from '../checkbox'
@@ -27,7 +27,7 @@ import * as S from './styled'
interface Props {
item: ItemType
status?: RowStatus
- checkStatus?: CheckStatus
+ checkStatus?: CheckStatus | Array<CheckStatus>
checkedCount?: number
onExpandItem?: (it: ItemType) => void
onSelectItem?: (it: ItemType) => void
@@ -37,7 +37,6 @@ export const Item = ({
item,
status = RowStatus.noselected,
checkStatus = CheckStatus.nochecked,
- checkedCount = 0,
onExpandItem,
onSelectItem
}: Props) => {
@@ -45,23 +44,23 @@ export const Item = ({
onExpandItem?.(item)
}
- const handleCheckboxClick = (e: React.MouseEvent<HTMLDivElement>) => {
- e.stopPropagation()
+ const handleCheckboxClick = (e: React.MouseEvent<HTMLLabelElement>) => {
+ if (item.type === ItemTypeEnum.LEAF) {
+ e.stopPropagation()
+ }
onSelectItem?.(item)
}
return (
<S.Wrapper
+ type={item.type}
selected={status === RowStatus.selected}
onClick={handleRowClick}
>
- <Checkbox status={checkStatus} onClick={handleCheckboxClick} />
- <span className='title'>{item.title}</span>
- {!!item.total && (
- <span className='count'>
- ({checkedCount}/{item.total})
- </span>
- )}
+ <Checkbox status={checkStatus} onClick={handleCheckboxClick}>
+ {item.title}
+ </Checkbox>
+ {item.type === ItemTypeEnum.BRANCH && <span className='indicator' />}
</S.Wrapper>
)
}
diff --git a/config-ui/src/components/miller-columns/components/item/styled.ts b/config-ui/src/components/miller-columns/components/item/styled.ts
index 7c2dab4b8..ca7b3476c 100644
--- a/config-ui/src/components/miller-columns/components/item/styled.ts
+++ b/config-ui/src/components/miller-columns/components/item/styled.ts
@@ -18,23 +18,33 @@
import styled from '@emotion/styled'
-export const Wrapper = styled.div<{ selected: boolean }>`
+import { ItemTypeEnum } from '../../types'
+
+export const Wrapper = styled.div<{ selected: boolean; type: ItemTypeEnum }>`
display: flex;
align-items: center;
+ justify-content: space-between;
padding: 4px 12px;
- cursor: pointer;
-
- ${({ selected }) => (selected ? 'background-color: #f5f5f7;' : '')}
- &:hover {
- background-color: #f5f5f7;
- }
+ ${({ type }) =>
+ type === ItemTypeEnum.BRANCH
+ ? `
+ cursor: pointer;
+ &:hover {
+ background-color: #f5f5f7;
+ }
+ `
+ : ''}
- & > span.name {
- font-size: 14px;
- }
+ ${({ selected }) => (selected ? 'background-color: #f5f5f7;' : '')}
- & > span.count {
- font-size: 14px;
+ & > span.indicator {
+ display: table;
+ width: 6px;
+ height: 6px;
+ border: 1px solid #000;
+ border-top: 0;
+ border-left: 0;
+ transform: rotate(-45deg);
}
`
diff --git a/config-ui/src/components/miller-columns/hooks/index.ts b/config-ui/src/components/miller-columns/hooks/index.ts
index d9c9dbfb1..25195b787 100644
--- a/config-ui/src/components/miller-columns/hooks/index.ts
+++ b/config-ui/src/components/miller-columns/hooks/index.ts
@@ -18,5 +18,5 @@
export * from './use-columns'
export * from './use-item-map'
+export * from './use-load-items'
export * from './use-miller-columns'
-export * from './use-test'
diff --git a/config-ui/src/components/miller-columns/hooks/use-item-map.ts b/config-ui/src/components/miller-columns/hooks/use-item-map.ts
index ad62205c0..89113d148 100644
--- a/config-ui/src/components/miller-columns/hooks/use-item-map.ts
+++ b/config-ui/src/components/miller-columns/hooks/use-item-map.ts
@@ -19,13 +19,23 @@
import { useMemo } from 'react'
import type { ItemType, ItemInfoType } from '../types'
+import { ItemStatusEnum } from '../types'
interface Props {
items: ItemType[]
- selectedItemIds?: Array<ItemType['id']>
}
-export const useItemMap = ({ items, selectedItemIds = [] }: Props) => {
+export const useItemMap = ({ items }: Props) => {
+ const checkChildLoaded = (item: ItemType): boolean => {
+ if (item.status === ItemStatusEnum.PENDING) {
+ return false
+ }
+
+ return item.items.every((it) => {
+ return checkChildLoaded(it)
+ })
+ }
+
return useMemo(() => {
const itemMap = new Map<ItemType['id'], ItemInfoType>()
@@ -40,7 +50,7 @@ export const useItemMap = ({ items, selectedItemIds = [] }: Props) => {
itemMap.set(item.id, {
item,
parentId: parent?.id,
- selectedChildCount: 0
+ childLoaded: checkChildLoaded(item)
})
}
@@ -50,30 +60,18 @@ export const useItemMap = ({ items, selectedItemIds = [] }: Props) => {
}
items.forEach((it) => collect({ item: it }))
- selectedItemIds.forEach((id) => {
- const childTotal = itemMap.get(id)?.item.total ?? 0
- const addedCount = childTotal + 1
- const parentId = itemMap.get(id)?.parentId
- const parent = parentId ? itemMap.get(parentId) : null
- if (parent) {
- parent.selectedChildCount += addedCount
- }
- })
return {
getItem(id: ItemType['id']) {
return (itemMap.get(id) as ItemInfoType).item
},
- getItemSelectedChildCount(id: ItemType['id']) {
- return (itemMap.get(id) as ItemInfoType).selectedChildCount
- },
getItemParent(id: ItemType['id']) {
const parentId = itemMap.get(id)?.parentId
return parentId ? (itemMap.get(parentId) as ItemInfoType).item : null
},
- getItemMapSize() {
- return itemMap.size
+ getItemChildLoaded(id: ItemType['id']) {
+ return (itemMap.get(id) as ItemInfoType).childLoaded
}
}
- }, [items, selectedItemIds])
+ }, [items])
}
diff --git a/config-ui/src/components/miller-columns/hooks/use-load-items.ts b/config-ui/src/components/miller-columns/hooks/use-load-items.ts
new file mode 100644
index 000000000..1a75873b8
--- /dev/null
+++ b/config-ui/src/components/miller-columns/hooks/use-load-items.ts
@@ -0,0 +1,100 @@
+/*
+ * 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 { ItemType, ItemTypeEnum, ItemStatusEnum } from '../types'
+
+interface Props {
+ getInitItems: () => Promise<Array<ItemType>>
+ loadMoreItems: (item: ItemType) => Promise<Array<ItemType>>
+}
+
+type TreeType<T> = Record<ItemType['id'], ItemType & T>
+
+export const useLoadItems = <T>({ getInitItems, loadMoreItems }: Props) => {
+ const [tree, setTree] = useState<TreeType<T>>({})
+
+ const itemsToTree = (items: Array<ItemType>) => {
+ return items.reduce((acc, cur) => {
+ acc[cur.id] = {
+ ...cur,
+ items: [],
+ status:
+ cur.type === ItemTypeEnum.BRANCH
+ ? ItemStatusEnum.PENDING
+ : ItemStatusEnum.READY
+ }
+ return acc
+ }, {} as any)
+ }
+
+ const treeToItems = (t: TreeType<T>) => {
+ if (!t.root) {
+ return []
+ }
+
+ const transform = (arr: Array<ItemType>): Array<ItemType> => {
+ return arr.map((it) => ({
+ ...it,
+ ...t[it.id],
+ items: transform(t[it.id].items)
+ }))
+ }
+
+ return transform(t.root.items)
+ }
+
+ useEffect(() => {
+ ;(async () => {
+ const initItems = await getInitItems()
+ setTree({
+ root: {
+ id: 'root',
+ title: 'root',
+ type: ItemTypeEnum.BRANCH,
+ status: ItemStatusEnum.READY,
+ items: initItems
+ },
+ ...itemsToTree(initItems)
+ })
+ })()
+ }, [])
+
+ return useMemo(() => {
+ return {
+ items: treeToItems(tree),
+ itemTree: tree,
+ async loadItems(item: ItemType) {
+ if (tree[item.id].status === ItemStatusEnum.READY) {
+ return
+ }
+ const items = await loadMoreItems(item)
+ setTree({
+ ...tree,
+ [`${item.id}`]: {
+ ...item,
+ items,
+ status: ItemStatusEnum.READY
+ },
+ ...itemsToTree(items)
+ })
+ }
+ }
+ }, [tree])
+}
diff --git a/config-ui/src/components/miller-columns/hooks/use-miller-columns.ts b/config-ui/src/components/miller-columns/hooks/use-miller-columns.ts
index 837f437c4..bc57cf79c 100644
--- a/config-ui/src/components/miller-columns/hooks/use-miller-columns.ts
+++ b/config-ui/src/components/miller-columns/hooks/use-miller-columns.ts
@@ -19,7 +19,7 @@
import { useState, useMemo, useEffect } from 'react'
import type { ItemType, ColumnType } from '../types'
-import { RowStatus } from '../types'
+import { ItemTypeEnum, RowStatus } from '../types'
import { CheckStatus } from '../components'
import { useItemMap } from './use-item-map'
@@ -29,21 +29,26 @@ export interface UseMillerColumnsProps {
items: ItemType[]
activeItemId?: ItemType['id']
onActiveItemId?: (id: ItemType['id']) => void
- selectedItemIds?: Array<ItemType['id']>
+ disabledItemIds?: Array<ItemType['id']>
+ selectedItemIds: Array<ItemType['id']>
onSelectedItemIds?: (ids: Array<ItemType['id']>) => void
+ onExpandItem?: (item: ItemType) => void
}
export const useMillerColumns = ({
items,
+ disabledItemIds,
onActiveItemId,
onSelectedItemIds,
+ onExpandItem,
...props
}: UseMillerColumnsProps) => {
const [activeItemId, setActiveItemId] = useState<ItemType['id']>()
- const [selectedItemIds, setSelectedItemIds] =
- useState<Array<ItemType['id']>>()
+ const [selectedItemIds, setSelectedItemIds] = useState<Array<ItemType['id']>>(
+ []
+ )
- const itemMap = useItemMap({ items, selectedItemIds })
+ const itemMap = useItemMap({ items })
const columns = useColumns({ items, itemMap, activeItemId })
useEffect(() => {
@@ -51,148 +56,100 @@ export const useMillerColumns = ({
}, [props.activeItemId])
useEffect(() => {
- setSelectedItemIds(props.selectedItemIds)
+ setSelectedItemIds(props.selectedItemIds ?? [])
}, [props.selectedItemIds])
+ const collectAddParentIds = (item: ItemType) => {
+ let result: Array<ItemType['id']> = []
+
+ const parentItem = itemMap.getItemParent(item.id)
+
+ if (parentItem) {
+ const childSelectedIds = parentItem.items
+ .map((it) => it.id)
+ .filter((id) => [...selectedItemIds, item.id].includes(id))
+
+ if (childSelectedIds.length === parentItem.items.length) {
+ result.push(parentItem.id)
+ result.push(...collectAddParentIds(parentItem))
+ }
+ }
+
+ return result
+ }
+
+ const collectRemoveParentIds = (item: ItemType) => {
+ let result: Array<ItemType['id']> = []
+
+ const parentItem = itemMap.getItemParent(item.id)
+
+ if (parentItem) {
+ result.push(parentItem.id)
+ result.push(...collectRemoveParentIds(parentItem))
+ }
+
+ return result
+ }
+
return useMemo(
() => ({
columns,
itemMap,
activeItemId,
selectedItemIds,
- getStatus(item: ItemType) {
- if (item.id === activeItemId) {
+ getStatus(item: ItemType, column: ColumnType) {
+ if (column.activeId === item.id) {
return RowStatus.selected
}
return RowStatus.noselected
},
getChekecdStatus(item: ItemType) {
- if (!selectedItemIds?.length) {
- return CheckStatus.nochecked
- }
- if (selectedItemIds?.includes(item.id)) {
- return CheckStatus.checked
- }
+ const childSelectedIds = item.items
+ .map((it) => it.id)
+ .filter((id) => selectedItemIds.includes(id))
- const hasChildCheckedIds = selectedItemIds.filter((id) =>
- item.items?.map((it) => it.id).includes(id)
- )
-
- if (!hasChildCheckedIds.length) {
- return CheckStatus.nochecked
- }
-
- if (hasChildCheckedIds.length === item.items?.length) {
- return CheckStatus.checked
- }
-
- return CheckStatus.indeterminate
- },
- getCheckedAllStatus(column: ColumnType) {
- const itemIds = column.items?.map((it) => it.id) ?? []
- const colSelectedIds = itemIds.filter((id) =>
- selectedItemIds?.includes(id)
- )
switch (true) {
- case colSelectedIds.length === itemIds.length:
+ case !itemMap.getItemChildLoaded(item.id):
+ case (disabledItemIds ?? []).includes(item.id):
+ return CheckStatus.disabled
+ case selectedItemIds.includes(item.id):
return CheckStatus.checked
- case !!colSelectedIds.length:
+ case !!childSelectedIds.length:
return CheckStatus.indeterminate
default:
return CheckStatus.nochecked
}
},
- getCheckedCount(item: ItemType) {
- return itemMap.getItemSelectedChildCount(item.id)
- },
onExpandItem(item: ItemType) {
- if (!item.items?.length) {
+ if (item.type !== ItemTypeEnum.BRANCH) {
return
}
+ onExpandItem?.(item)
onActiveItemId ? onActiveItemId(item.id) : setActiveItemId(item.id)
},
onSelectItem(item: ItemType) {
- let newIds: Array<ItemType['id']>
- let targetIds: Array<ItemType['id']> = [item.id]
- const itemIds = item.items?.map((it) => it.id) ?? []
-
- const collect = (id: ItemType['id']) => {
- targetIds.push(id)
- const item = itemMap.getItem(id)
- if (item.items) {
- item.items.forEach((it) => collect(it.id))
- }
- }
-
- itemIds.forEach((id) => collect(id))
-
- const isRemoveExistedItem = !!selectedItemIds?.includes(item.id)
-
- if (isRemoveExistedItem) {
- const parentItem = itemMap.getItemParent(item.id)
- const deleteIds = [parentItem?.id, ...targetIds].filter(Boolean)
- newIds =
- selectedItemIds?.filter((id) => !deleteIds.includes(id)) ?? []
- } else {
- const parentItem = itemMap.getItemParent(item.id)
- const addIds = targetIds.filter(
- (id) => !selectedItemIds?.includes(id)
- )
-
- if (parentItem) {
- const parentChildIds = parentItem.items?.map((it) => it.id) ?? []
- const parentSelectedIds = parentChildIds.filter((id) =>
- [...(selectedItemIds ?? []), item.id].includes(id)
- )
-
- const isAllChildSelected =
- parentSelectedIds.length === parentItem?.items?.length
-
- if (isAllChildSelected) {
- addIds.push(parentItem.id)
- }
- }
+ let newIds: Array<ItemType['id']> = [item.id]
+ const isRemoveExistedItem = !!selectedItemIds.includes(item.id)
- newIds = [...(selectedItemIds ?? []), ...addIds]
+ const collectChildIds = (it: ItemType) => {
+ newIds.push(it.id)
+ it.items.forEach((it) => collectChildIds(it))
}
- onSelectedItemIds
- ? onSelectedItemIds(newIds)
- : setSelectedItemIds(newIds)
- },
- onSelectAllItem(column: ColumnType) {
- let newIds: Array<ItemType['id']>
- let targetIds: Array<ItemType['id']> = []
- const itemIds = column.items?.map((it) => it.id) ?? []
-
- const collect = (id: ItemType['id']) => {
- targetIds.push(id)
- const item = itemMap.getItem(id)
- if (item.items) {
- item.items.forEach((it) => collect(it.id))
- }
- }
+ item.items.forEach((it) => collectChildIds(it))
- itemIds.forEach((id) => collect(id))
-
- const isRemoveExistedItems =
- itemIds.filter((id) => selectedItemIds?.includes(id)).length ===
- itemIds.length
-
- if (isRemoveExistedItems) {
- const deleteIds = [...targetIds, column.parentId].filter(Boolean)
- newIds =
- selectedItemIds?.filter((id) => !deleteIds.includes(id)) ?? []
+ if (!isRemoveExistedItem) {
+ newIds = [
+ ...new Set([
+ ...newIds,
+ ...selectedItemIds,
+ ...collectAddParentIds(item)
+ ])
+ ]
} else {
- const addIds = targetIds.filter(
- (id) => !selectedItemIds?.includes(id)
+ newIds = selectedItemIds.filter(
+ (id) => ![...newIds, ...collectRemoveParentIds(item)].includes(id)
)
-
- if (column.parentId) {
- addIds.push(column.parentId)
- }
-
- newIds = [...(selectedItemIds ?? []), ...addIds]
}
onSelectedItemIds
@@ -200,6 +157,6 @@ export const useMillerColumns = ({
: setSelectedItemIds(newIds)
}
}),
- [columns, itemMap, activeItemId, selectedItemIds]
+ [columns, itemMap, activeItemId, disabledItemIds, selectedItemIds]
)
}
diff --git a/config-ui/src/components/miller-columns/hooks/use-test.ts b/config-ui/src/components/miller-columns/hooks/use-test.ts
deleted file mode 100644
index c7149f1af..000000000
--- a/config-ui/src/components/miller-columns/hooks/use-test.ts
+++ /dev/null
@@ -1,116 +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 { useMemo, useState } from 'react'
-
-const items = [
- {
- id: 1,
- title: 'merico-dev',
- total: 13,
- items: [
- {
- id: 11,
- title: 'devlake'
- },
- {
- id: 12,
- title: 'devstream'
- },
- {
- id: 13,
- title: 'another-repo'
- },
- {
- id: 14,
- title: 'repo2'
- },
- {
- id: 15,
- title: 'repo2'
- },
- {
- id: 16,
- title: 'repo2'
- },
- {
- id: 17,
- title: 'repo2'
- },
- {
- id: 18,
- title: 'repo2'
- },
- {
- id: 19,
- title: 'ae-repo',
- total: 4,
- items: [
- {
- id: 191,
- title: 'ae-repo-1'
- },
- {
- id: 192,
- title: 'ae-repo-2'
- },
- {
- id: 193,
- title: 'ae-repo-child',
- total: 1,
- items: [
- {
- id: 1931,
- title: 'ae-repo-child-1'
- }
- ]
- }
- ]
- }
- ]
- },
- {
- id: 2,
- title: 'mintsweet',
- total: 2,
- items: [
- {
- id: 21,
- title: 'reate'
- },
- {
- id: 22,
- title: 'mst-advanced'
- }
- ]
- },
- {
- id: 3,
- title: 'test'
- }
-]
-
-export const useTest = () => {
- const [ids, setIds] = useState([])
-
- console.log(ids)
-
- return useMemo(() => {
- return { items, ids, setIds }
- }, [items, ids, setIds])
-}
diff --git a/config-ui/src/components/miller-columns/index.ts b/config-ui/src/components/miller-columns/index.ts
index 07cc2c3f3..3db209c1a 100644
--- a/config-ui/src/components/miller-columns/index.ts
+++ b/config-ui/src/components/miller-columns/index.ts
@@ -17,4 +17,6 @@
*/
export * from './miller-columns'
-export * from './hooks/use-test'
+export * from './components'
+export * from './hooks'
+export * from './types'
diff --git a/config-ui/src/components/miller-columns/miller-columns.tsx b/config-ui/src/components/miller-columns/miller-columns.tsx
index ce1948eb4..7aadb9a95 100644
--- a/config-ui/src/components/miller-columns/miller-columns.tsx
+++ b/config-ui/src/components/miller-columns/miller-columns.tsx
@@ -19,66 +19,50 @@
import React from 'react'
import { useMillerColumns, UseMillerColumnsProps } from './hooks'
-import { Item, ItemAll } from './components'
+import { Column, Item } from './components'
+import { ColumnType } from './types'
import * as S from './styled'
interface Props extends UseMillerColumnsProps {
height?: number
firstColumnTitle?: React.ReactNode
+ renderColumnBottom?: (col: ColumnType) => React.ReactNode
}
export const MillerColumns = ({
- items,
- activeItemId,
- onActiveItemId,
- selectedItemIds,
- onSelectedItemIds,
firstColumnTitle,
+ height,
+ renderColumnBottom,
...props
}: Props) => {
- const {
- columns,
- getStatus,
- getChekecdStatus,
- getCheckedCount,
- onExpandItem,
- onSelectItem,
- getCheckedAllStatus,
- onSelectAllItem
- } = useMillerColumns({
- items,
- activeItemId,
- onActiveItemId,
- selectedItemIds,
- onSelectedItemIds
- })
+ const { columns, getStatus, getChekecdStatus, onExpandItem, onSelectItem } =
+ useMillerColumns(props)
return (
- <S.Container {...props}>
- {columns.map((col, i) => (
- <div key={col.parentId} className='items'>
- {i === 0 && firstColumnTitle && (
- <div className='title'>{firstColumnTitle}</div>
- )}
- <ItemAll
- column={col}
- checkStatus={getCheckedAllStatus(col)}
- onSelectAllItem={onSelectAllItem}
+ <S.Container>
+ {columns.map((col, i) => {
+ const bottom = renderColumnBottom?.(col)
+ return (
+ <Column
+ key={col.parentId}
+ items={col.items}
+ renderItem={(item) => (
+ <Item
+ key={item.id}
+ item={item}
+ status={getStatus(item, col)}
+ checkStatus={getChekecdStatus(item)}
+ onExpandItem={onExpandItem}
+ onSelectItem={onSelectItem}
+ />
+ )}
+ height={height}
+ title={i === 0 && firstColumnTitle}
+ bottom={bottom}
/>
- {col.items?.map((it) => (
- <Item
- key={it.id}
- item={it}
- status={getStatus(it)}
- checkStatus={getChekecdStatus(it)}
- checkedCount={getCheckedCount(it)}
- onExpandItem={onExpandItem}
- onSelectItem={onSelectItem}
- />
- ))}
- </div>
- ))}
+ )
+ })}
</S.Container>
)
}
diff --git a/config-ui/src/components/miller-columns/styled.ts b/config-ui/src/components/miller-columns/styled.ts
index f22fb81d2..59f117570 100644
--- a/config-ui/src/components/miller-columns/styled.ts
+++ b/config-ui/src/components/miller-columns/styled.ts
@@ -18,29 +18,10 @@
import styled from '@emotion/styled'
-export const Container = styled.div<{ height?: number }>`
+export const Container = styled.div`
display: flex;
width: 100%;
- ${({ height }) => `height: ${height}px;`}
+ border: 1px solid #dbe4fd;
+ border-radius: 4px;
overflow-x: auto;
-
- .items {
- flex: 1;
- margin: 0;
- padding: 0;
- list-style: none;
- border: 1px solid #dbe4fd;
- border-right: none;
- border-radius: 4px;
-
- &:last-child {
- border-right: 1px solid #dbe4fd;
- }
-
- & > .title {
- padding: 4px 12px;
- font-weight: 700;
- color: #292b3f;
- }
- }
`
diff --git a/config-ui/src/components/miller-columns/types.ts b/config-ui/src/components/miller-columns/types.ts
index 2624f8887..4f830f354 100644
--- a/config-ui/src/components/miller-columns/types.ts
+++ b/config-ui/src/components/miller-columns/types.ts
@@ -16,28 +16,39 @@
*
*/
+export enum ItemTypeEnum {
+ LEAF = 'leaf',
+ BRANCH = 'branch'
+}
+
+export enum ItemStatusEnum {
+ PENDING = 'pending',
+ READY = 'ready'
+}
+
export type ItemType = {
id: string | number
title: string
- total?: number
- items?: ItemType[]
+ type: ItemTypeEnum
+ status: ItemStatusEnum
+ items: ItemType[]
}
export type ItemInfoType = {
item: ItemType
parentId?: ItemType['id']
- selectedChildCount: number
+ childLoaded: boolean
}
export type ItemMapType = {
getItem: (id: ItemType['id']) => ItemType
- getItemSelectedChildCount: (id: ItemType['id']) => number
getItemParent: (id: ItemType['id']) => ItemType | null
+ getItemChildLoaded: (id: ItemType['id']) => boolean
}
export type ColumnType = {
parentId: ItemType['id'] | null
- items?: ItemType[]
+ items: ItemType[]
activeId: ItemType['id'] | null
}
diff --git a/config-ui/src/config/gitlabApiProxy.js b/config-ui/src/config/gitlabApiProxy.js
deleted file mode 100644
index a67218258..000000000
--- a/config-ui/src/config/gitlabApiProxy.js
+++ /dev/null
@@ -1,23 +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.
- *
- */
-// @todo: add string replacer for [:connectionId] or refactor this const
-const GITLAB_API_PROXY_ENDPOINT =
- '/api/plugins/gitlab/connections/[:connectionId:]/proxy/rest'
-const PROJECTS_ENDPOINT = `${GITLAB_API_PROXY_ENDPOINT}/projects?search=[:search:]&membership=[:membership:]`
-
-export { GITLAB_API_PROXY_ENDPOINT, PROJECTS_ENDPOINT }
diff --git a/config-ui/src/hooks/useGitlab.jsx b/config-ui/src/hooks/useGitlab.jsx
deleted file mode 100644
index 1736c8717..000000000
--- a/config-ui/src/hooks/useGitlab.jsx
+++ /dev/null
@@ -1,100 +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 { useEffect, useState, useCallback } from 'react'
-import request from '@/utils/request'
-import { ToastNotification } from '@/components/Toast'
-
-const useGitlab = (
- { apiProxyPath, projectsEndpoint },
- activeConnection = null
-) => {
- const [isFetching, setIsFetching] = useState(false)
- const [projects, setProjects] = useState([])
- const [error, setError] = useState()
-
- const fetchProjects = useCallback(
- async (search = '', onlyQueryMemberRepo = true) => {
- try {
- if (apiProxyPath.includes('null')) {
- throw new Error('Connection ID is Null')
- }
- setError(null)
- setIsFetching(true)
- if (search.length > 2) {
- // only search when type more than 2 chars
- const endpoint = projectsEndpoint
- .replace('[:connectionId:]', activeConnection?.connectionId)
- .replace('[:search:]', search)
- .replace('[:membership:]', onlyQueryMemberRepo ? 1 : 0)
- const projectsResponse = await request.get(endpoint)
- if (
- projectsResponse &&
- projectsResponse.status === 200 &&
- projectsResponse.data
- ) {
- setProjects(createListData(projectsResponse.data))
- } else {
- throw new Error('request projects fail')
- }
- } else {
- setProjects([])
- }
- } catch (e) {
- setError(e)
- ToastNotification.show({
- message: e.message,
- intent: 'danger',
- icon: 'error'
- })
- } finally {
- setIsFetching(false)
- }
- },
- [projectsEndpoint, activeConnection, apiProxyPath]
- )
-
- const createListData = (
- data = [],
- titleProperty = 'name_with_namespace',
- valueProperty = 'id',
- iconProperty = 'avatar_url'
- ) => {
- return data.map((d, dIdx) => ({
- id: d[valueProperty],
- key: d[valueProperty],
- title: d[titleProperty],
- shortTitle: d.name,
- value: d[valueProperty],
- icon: d[iconProperty],
- type: 'string'
- }))
- }
-
- useEffect(() => {
- console.log('>>> GITLAB API PROXY: FIELD SELECTOR PROJECTS DATA', projects)
- }, [projects])
-
- return {
- isFetching,
- fetchProjects,
- projects,
- error
- }
-}
-
-export default useGitlab
diff --git a/config-ui/src/models/GitlabProject.js b/config-ui/src/models/GitlabProject.js
index 91a109012..4d4bcf329 100644
--- a/config-ui/src/models/GitlabProject.js
+++ b/config-ui/src/models/GitlabProject.js
@@ -63,6 +63,7 @@ class GitlabProject extends Entity {
this.title = data?.title || this.name || this.id || null
this.shortTitle = data?.shortTitle || null
this.icon = data?.icon || null
+ this.type = data?.type || null
// @todo: GitLab API props to camelCase
this.visibility = data?.visibility || 'private'
diff --git a/config-ui/src/pages/blueprints/blueprint-settings.jsx b/config-ui/src/pages/blueprints/blueprint-settings.jsx
index 3c8e00c21..0f9e3e728 100644
--- a/config-ui/src/pages/blueprints/blueprint-settings.jsx
+++ b/config-ui/src/pages/blueprints/blueprint-settings.jsx
@@ -64,11 +64,6 @@ import BlueprintDataScopesDialog from '@/components/blueprints/BlueprintDataScop
import BlueprintNavigationLinks from '@/components/blueprints/BlueprintNavigationLinks'
import DataScopesGrid from '@/components/blueprints/DataScopesGrid'
import AdvancedJSON from '@/components/blueprints/create-workflow/AdvancedJSON'
-import useGitlab from '@/hooks/useGitlab'
-import {
- GITLAB_API_PROXY_ENDPOINT,
- PROJECTS_ENDPOINT
-} from '@/config/gitlabApiProxy'
import useJenkins from '@/hooks/useJenkins'
import {
JENKINS_API_PROXY_ENDPOINT,
@@ -306,19 +301,6 @@ const BlueprintSettings = (props) => {
configuredConnection
)
- const {
- fetchProjects: fetchGitlabProjects,
- projects: gitlabProjects,
- isFetching: isFetchingGitlab,
- error: gitlabProxyError
- } = useGitlab(
- {
- apiProxyPath: GITLAB_API_PROXY_ENDPOINT,
- projectsEndpoint: PROJECTS_ENDPOINT
- },
- configuredConnection
- )
-
const {
fetchJobs: fetchJenkinsJobs,
jobs: jenkinsJobs,
@@ -727,6 +709,7 @@ const BlueprintSettings = (props) => {
break
}
}, [
+ skipOnFail,
blueprintName,
cronConfig,
customCronConfig,
@@ -1098,7 +1081,6 @@ const BlueprintSettings = (props) => {
loading={
isFetchingBlueprint ||
isFetchingJIRA ||
- isFetchingGitlab ||
isFetchingJenkins
}
/>
@@ -1141,7 +1123,6 @@ const BlueprintSettings = (props) => {
loading={
isFetchingBlueprint ||
isFetchingJIRA ||
- isFetchingGitlab ||
isFetchingJenkins
}
/>
@@ -1268,10 +1249,6 @@ const BlueprintSettings = (props) => {
fieldsList={jiraApiFields}
isFetching={isFetchingBlueprint}
isFetchingJIRA={isFetchingJIRA}
- fetchGitlabProjects={fetchGitlabProjects}
- gitlabProjects={gitlabProjects}
- isFetchingGitlab={isFetchingGitlab}
- gitlabProxyError={gitlabProxyError}
fetchJenkinsJobs={fetchJenkinsJobs}
jenkinsJobs={jenkinsJobs}
isFetchingJenkins={isFetchingJenkins}
diff --git a/config-ui/src/pages/blueprints/create-blueprint.jsx b/config-ui/src/pages/blueprints/create-blueprint.jsx
index 0a20f34bf..adadea672 100644
--- a/config-ui/src/pages/blueprints/create-blueprint.jsx
+++ b/config-ui/src/pages/blueprints/create-blueprint.jsx
@@ -39,10 +39,6 @@ import { WorkflowAdvancedSteps, WorkflowSteps } from '@/data/BlueprintWorkflow'
import { DEVLAKE_ENDPOINT } from '@/utils/config'
import request from '@/utils/request'
-import {
- GITLAB_API_PROXY_ENDPOINT,
- PROJECTS_ENDPOINT
-} from '@/config/gitlabApiProxy'
import {
JENKINS_API_PROXY_ENDPOINT,
JENKINS_JOBS_ENDPOINT
@@ -58,7 +54,6 @@ import usePipelineValidation from '@/hooks/usePipelineValidation'
import useConnectionValidation from '@/hooks/useConnectionValidation'
import useJIRA from '@/hooks/useJIRA'
import useJenkins from '@/hooks/useJenkins'
-import useGitlab from '@/hooks/useGitlab'
import WorkflowStepsBar from '@/components/blueprints/WorkflowStepsBar'
import WorkflowActions from '@/components/blueprints/WorkflowActions'
@@ -284,19 +279,6 @@ const CreateBlueprint = (props) => {
configuredConnection
)
- const {
- fetchProjects: fetchGitlabProjects,
- projects: gitlabProjects,
- isFetching: isFetchingGitlab,
- error: gitlabProxyError
- } = useGitlab(
- {
- apiProxyPath: GITLAB_API_PROXY_ENDPOINT,
- projectsEndpoint: PROJECTS_ENDPOINT
- },
- configuredConnection
- )
-
const {
fetchJobs: fetchJenkinsJobs,
jobs: jenkinsJobs,
@@ -978,9 +960,6 @@ const CreateBlueprint = (props) => {
activeConnectionTab={activeConnectionTab}
blueprintConnections={blueprintConnections}
jiraBoards={jiraBoards}
- fetchGitlabProjects={fetchGitlabProjects}
- isFetchingGitlab={isFetchingGitlab}
- gitlabProjects={gitlabProjects}
fetchJenkinsJobs={fetchJenkinsJobs}
isFetchingJenkins={isFetchingJenkins}
jenkinsJobs={jenkinsJobs}
@@ -1000,7 +979,6 @@ const CreateBlueprint = (props) => {
]}
isFetching={
isFetchingJIRA ||
- isFetchingGitlab ||
isFetchingJenkins ||
isFetchingConnection
}
@@ -1080,7 +1058,6 @@ const CreateBlueprint = (props) => {
isLoading={
isSaving ||
isFetchingJIRA ||
- isFetchingGitlab ||
isFetchingJenkins ||
isFetchingConnection ||
isTestingConnection
diff --git a/config-ui/tsconfig.json b/config-ui/tsconfig.json
index 9f8365997..fa0074186 100644
--- a/config-ui/tsconfig.json
+++ b/config-ui/tsconfig.json
@@ -1,5 +1,9 @@
{
"compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"]
+ },
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],