You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@devlake.apache.org by kl...@apache.org on 2022/08/29 15:53:20 UTC
[incubator-devlake] branch main updated: feat: create paginator hook and paginate blueprints grid (#2780)
This is an automated email from the ASF dual-hosted git repository.
klesh 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 e90db240 feat: create paginator hook and paginate blueprints grid (#2780)
e90db240 is described below
commit e90db240ab73178dc4dd564064b22d434913963e
Author: Julien Chinapen <ju...@merico.dev>
AuthorDate: Mon Aug 29 11:53:15 2022 -0400
feat: create paginator hook and paginate blueprints grid (#2780)
* feat: create paginator hook
* feat: finish paginator
* feat: add filter for pageComponent
* fix: fix for review
* fix: rename setData in page components
* fix: fix for lint
Co-authored-by: linyh <ya...@meri.co>
---
.../src/components/blueprints/BlueprintsGrid.jsx | 5 +-
.../create-workflow/DataTransformations.jsx | 5 +-
config-ui/src/hooks/useConnectionManager.jsx | 2 +-
config-ui/src/hooks/usePaginator.jsx | 209 +++++++++++++++++++++
config-ui/src/pages/blueprints/index.jsx | 52 ++---
5 files changed, 242 insertions(+), 31 deletions(-)
diff --git a/config-ui/src/components/blueprints/BlueprintsGrid.jsx b/config-ui/src/components/blueprints/BlueprintsGrid.jsx
index 5c1bee38..2b5d302b 100644
--- a/config-ui/src/components/blueprints/BlueprintsGrid.jsx
+++ b/config-ui/src/components/blueprints/BlueprintsGrid.jsx
@@ -37,7 +37,6 @@ import DeletePopover from '@/components/blueprints/DeletePopover'
const BlueprintsGrid = (props) => {
const {
blueprints = [],
- filteredBlueprints = [],
pipelines = [],
activeBlueprint,
blueprintSchedule,
@@ -152,7 +151,7 @@ const BlueprintsGrid = (props) => {
minWidth: '830px'
}}
>
- {(activeFilterStatus ? filteredBlueprints : blueprints).map((b, bIdx) => (
+ {(blueprints).map((b, bIdx) => (
<div key={`blueprint-row-key-${bIdx}`}>
<div
style={{
@@ -481,7 +480,7 @@ const BlueprintsGrid = (props) => {
</Collapse>
</div>
))}
- {(activeFilterStatus ? filteredBlueprints.length === 0 : blueprints.length === 0) && (
+ {(blueprints.length === 0) && (
<div style={{ padding: '12px' }}>
<h3 style={{
fontWeight: 800,
diff --git a/config-ui/src/components/blueprints/create-workflow/DataTransformations.jsx b/config-ui/src/components/blueprints/create-workflow/DataTransformations.jsx
index 197b2050..bba7e3cc 100644
--- a/config-ui/src/components/blueprints/create-workflow/DataTransformations.jsx
+++ b/config-ui/src/components/blueprints/create-workflow/DataTransformations.jsx
@@ -387,8 +387,9 @@ const DataTransformations = (props) => {
<Tooltip
position={Position.TOP}
intent={Intent.PRIMARY}
- content={'Close Editor to Continue'}>
- <Spinner size={12} />
+ content='Close Editor to Continue'
+ >
+ <Spinner size={12} />
</Tooltip>
)}
/>
diff --git a/config-ui/src/hooks/useConnectionManager.jsx b/config-ui/src/hooks/useConnectionManager.jsx
index 8ed2f3bd..cfeea122 100644
--- a/config-ui/src/hooks/useConnectionManager.jsx
+++ b/config-ui/src/hooks/useConnectionManager.jsx
@@ -588,7 +588,7 @@ function useConnectionManager (
setPassword(activeConnection.password)
setProxy(activeConnection.Proxy || activeConnection.proxy)
setRateLimit(activeConnection.rateLimitPerHour)
- break
+ break
}
ToastNotification.clear()
// ToastNotification.show({ message: `Fetched settings for ${activeConnection.name}.`, intent: 'success', icon: 'small-tick' })
diff --git a/config-ui/src/hooks/usePaginator.jsx b/config-ui/src/hooks/usePaginator.jsx
new file mode 100644
index 00000000..19a97e0d
--- /dev/null
+++ b/config-ui/src/hooks/usePaginator.jsx
@@ -0,0 +1,209 @@
+/*
+ * 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, useEffect, useRef, useCallback, useMemo } from 'react'
+import {
+ Popover,
+ Menu,
+ MenuItem,
+ Button
+} from '@blueprintjs/core'
+
+import { integrationsData } from '@/data/integrations'
+
+const PagingOptionsMenu = (props) => {
+ const { pageOptions = [10, 25, 50, 75, 100], perPage = 10, setPerPage = (page) => undefined } = props
+ return (
+ <Menu>
+ {pageOptions && pageOptions.map(pageOption => (
+ <MenuItem
+ key={pageOption}
+ active={perPage === pageOption}
+ icon='key-option'
+ text={`${pageOption} Records`}
+ onClick={() => setPerPage(pageOption)}
+ />))}
+ </Menu>
+ )
+}
+
+const Controls = (props) => {
+ const {
+ enabled = true,
+ pagingOptionsMenu,
+ currentPage,
+ perPage = 10,
+ maxPage,
+ onPrevPage = () => {},
+ onNextPage = () => {},
+ isLoading = false,
+ } = props
+
+ return (
+ <div
+ className='pagination-controls'
+ style={{ display: 'flex', whiteSpace: 'nowrap' }}
+ >
+ <Popover placement='bottom'>
+ <Button
+ className='btn-select-page-size'
+ style={{ whiteSpace: 'nowrap' }}
+ icon='numbered-list'
+ text={`Rows: ${perPage}`}
+ disabled={isLoading}
+ outlined
+ minimal
+ />
+ <>{pagingOptionsMenu}</>
+ </Popover>
+ <Button
+ onClick={onPrevPage}
+ className='pagination-btn btn-prev-page'
+ icon='step-backward'
+ small
+ text='PREV'
+ style={{ marginLeft: '5px', marginRight: '5px', whiteSpace: 'nowrap' }}
+ disabled={currentPage === 1 || isLoading}
+ />
+ <Button
+ style={{ whiteSpace: 'nowrap' }}
+ disabled={currentPage === maxPage || isLoading}
+ onClick={onNextPage}
+ className='pagination-btn btn-next-page'
+ rightIcon='step-forward'
+ text='NEXT'
+ small
+ />
+ </div>
+ )
+}
+
+function usePaginator (initialLoadingState = false) {
+ // const [integrations, setIntegrations] = useState(integrationsData)
+
+ const [data, setData] = useState([])
+
+ // filter related
+ const [filterParams, setFilterParams] = useState({})
+ const [filterFunc, setFilterFunc] = useState(() => (params, item) => true)
+ const filteredData = useMemo(() => {
+ console.log('>> SET FILTER DATA BY', filterParams, data)
+ const filteredData = []
+ for (const item of data) {
+ if (filterFunc(filterParams, item)) {
+ filteredData.push(item)
+ }
+ }
+ return filteredData
+ }, [data, filterParams, filterFunc])
+
+ // page related
+ const [pagedData, setPagedData] = useState([])
+ const [pageOptions, setPageOptions] = useState([5, 25, 50, 75, 100])
+ const [currentPage, setCurrentPage] = useState(1)
+ const [perPage, setPerPage] = useState(pageOptions[1])
+ const maxPage = useMemo(() => Math.max(1, Math.ceil(filteredData.length / perPage)), [filteredData, perPage])
+
+ // others
+ const [isLoading, setIsLoading] = useState(initialLoadingState || false)
+ const [isProcessing, setIsProcessing] = useState(false)
+ const [refresh, setRefresh] = useState(false)
+
+ const setDataWithDefault = useCallback((data) => {
+ console.log('>> SET ALL DATA...', data)
+ setData(data || [])
+ }, [setData])
+
+ const goNextPage = useCallback(() => {
+ console.log('>>> PAGINATOR: GO NEXT PAGE ...')
+ setCurrentPage(currentPage => Math.min(maxPage, currentPage + 1))
+ // setRefresh((r) => !r)
+ console.log('>>>> NEXT PAGE', currentPage)
+ }, [maxPage, currentPage])
+
+ const goPrevPage = useCallback(() => {
+ console.log('>>> PAGINATOR: GO PREV PAGE ...')
+ setCurrentPage(currentPage => Math.max(1, currentPage - 1))
+ // setRefresh((r) => !r)
+ console.log('>>>> PREV PAGE', currentPage)
+ }, [currentPage])
+
+ const changePerPage = useCallback((perPage) => {
+ setPerPage(perPage)
+ setCurrentPage(1)
+ }, [setPerPage])
+
+ const resetPage = useCallback(() => {
+ setCurrentPage(1)
+ }, [setCurrentPage])
+
+ const paginateData = useCallback(() => {
+ console.log('>> FILTERED DATA...', filteredData)
+ const sliceBegin = (currentPage - 1) * perPage
+ const sliceEnd = currentPage * perPage
+ setPagedData(filteredData.slice(sliceBegin, sliceEnd))
+ console.log('>> PAGED DATA = ', filteredData.slice(sliceBegin, sliceEnd))
+ }, [filteredData, perPage, currentPage, setPagedData])
+
+ useEffect(() => {
+ paginateData()
+ }, [/* refresh, */perPage, filteredData, currentPage, paginateData])
+
+ useEffect(() => {
+ setCurrentPage(currentPage => Math.min(maxPage, currentPage))
+ }, [maxPage, setCurrentPage])
+
+ const renderControlsComponent = useCallback(() => {
+ return (
+ <Controls
+ currentPage={currentPage}
+ onNextPage={goNextPage}
+ onPrevPage={goPrevPage}
+ maxPage={maxPage}
+ perPage={perPage}
+ isLoading={isLoading}
+ pagingOptionsMenu={
+ <PagingOptionsMenu pageOptions={pageOptions} perPage={perPage} setPerPage={changePerPage} />
+ }
+ />
+ )
+ }, [currentPage, maxPage, perPage, pageOptions, isLoading, goNextPage, goPrevPage, changePerPage])
+
+ return {
+ goNextPage,
+ goPrevPage,
+ resetPage,
+ setPageOptions,
+ renderControlsComponent,
+ isLoading,
+ isProcessing,
+ data,
+ filteredData,
+ pagedData,
+ perPage,
+ maxPage,
+ setData: setDataWithDefault,
+ setPagedData,
+ setFilterParams,
+ setFilterFunc,
+ // setMaxPage,
+ setIsLoading,
+ setIsProcessing,
+ }
+}
+
+export default usePaginator
diff --git a/config-ui/src/pages/blueprints/index.jsx b/config-ui/src/pages/blueprints/index.jsx
index 95d7c463..288450ba 100644
--- a/config-ui/src/pages/blueprints/index.jsx
+++ b/config-ui/src/pages/blueprints/index.jsx
@@ -32,6 +32,7 @@ import {
import usePipelineManager from '@/hooks/usePipelineManager'
import useBlueprintManager from '@/hooks/useBlueprintManager'
import useBlueprintValidation from '@/hooks/useBlueprintValidation'
+import usePaginator from '@/hooks/usePaginator'
import Nav from '@/components/Nav'
import Sidebar from '@/components/Sidebar'
import AppCrumbs from '@/components/Breadcrumbs'
@@ -89,6 +90,14 @@ const Blueprints = (props) => {
detectPipelineProviders
} = usePipelineManager()
+ const {
+ pagedData,
+ setFilterParams,
+ setFilterFunc,
+ setData: setPaginatorData,
+ renderControlsComponent: renderPagnationControls
+ } = usePaginator()
+
const [expandDetails, setExpandDetails] = useState(false)
const [activeBlueprint, setActiveBlueprint] = useState(null)
const [draftBlueprint, setDraftBlueprint] = useState(null)
@@ -98,7 +107,6 @@ const Blueprints = (props) => {
const [pipelineTemplates, setPipelineTemplates] = useState([])
const [selectedPipelineTemplate, setSelectedPipelineTemplate] = useState()
- const [filteredBlueprints, setFilteredBlueprints] = useState([])
const [activeFilterStatus, setActiveFilterStatus] = useState()
const [relatedPipelines, setRelatedPipelines] = useState([])
@@ -239,37 +247,27 @@ const Blueprints = (props) => {
}, [fetchAllPipelines])
useEffect(() => {
- if (activeFilterStatus) {
+ setFilterFunc(() => (activeFilterStatus, blueprint) => {
switch (activeFilterStatus) {
case 'hourly':
- setFilteredBlueprints(blueprints.filter(b => b.cronConfig === getCronPreset(activeFilterStatus).cronConfig))
- break
case 'daily':
- setFilteredBlueprints(blueprints.filter(b => b.cronConfig === getCronPreset(activeFilterStatus).cronConfig))
- break
case 'weekly':
- setFilteredBlueprints(blueprints.filter(b => b.cronConfig === getCronPreset(activeFilterStatus).cronConfig))
- break
case 'monthly':
- setFilteredBlueprints(blueprints.filter(b => b.cronConfig === getCronPreset(activeFilterStatus).cronConfig))
- break
+ console.log(blueprint.cronConfig === getCronPreset(activeFilterStatus).cronConfig)
+ return blueprint.cronConfig === getCronPreset(activeFilterStatus).cronConfig
case 'manual':
- setFilteredBlueprints(blueprints.filter(b => b.isManual))
- break
+ return blueprint.isManual
case 'custom':
- setFilteredBlueprints(blueprints.filter(
- b => b.cronConfig !== getCronPreset('hourly').cronConfig &&
- b.cronConfig !== getCronPreset('daily').cronConfig &&
- b.cronConfig !== getCronPreset('weekly').cronConfig &&
- b.cronConfig !== getCronPreset('monthly').cronConfig
- ))
- break
+ return blueprint.cronConfig !== getCronPreset('hourly').cronConfig &&
+ blueprint.cronConfig !== getCronPreset('daily').cronConfig &&
+ blueprint.cronConfig !== getCronPreset('weekly').cronConfig &&
+ blueprint.cronConfig !== getCronPreset('monthly').cronConfig
default:
- // NO FILTERS
- break
+ return true
}
- }
- }, [activeFilterStatus, blueprints, getCronPreset])
+ })
+ setFilterParams(activeFilterStatus)
+ }, [activeFilterStatus, setFilterParams, getCronPreset])
// useEffect(() => {
// if (Array.isArray(tasks)) {
@@ -282,6 +280,10 @@ const Blueprints = (props) => {
console.log('>>>> DETECTED PROVIDERS TASKS....', detectedProviderTasks)
}, [detectedProviderTasks])
+ useEffect(() => {
+ setPaginatorData(blueprints)
+ }, [blueprints])
+
return (
<>
<div className='container'>
@@ -316,9 +318,8 @@ const Blueprints = (props) => {
{(!isFetchingBlueprints) && blueprints.length > 0 && (
<>
<BlueprintsGrid
- blueprints={blueprints}
+ blueprints={pagedData}
pipelines={relatedPipelines}
- filteredBlueprints={filteredBlueprints}
activeFilterStatus={activeFilterStatus}
onFilter={setActiveFilterStatus}
activeBlueprint={activeBlueprint}
@@ -381,6 +382,7 @@ const Blueprints = (props) => {
/>
</Card>
)}
+ <div style={{ alignSelf: 'flex-end', padding: '10px' }}>{renderPagnationControls()}</div>
</main>
</Content>
</div>