mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: connect sort table to backend (#5338)
Now FE sorting is done in backend.
This commit is contained in:
		
							parent
							
								
									db77962a72
								
							
						
					
					
						commit
						4e1040c849
					
				| @ -63,11 +63,18 @@ import { ListItemType } from './ProjectFeatureToggles.types'; | ||||
| import { createFeatureToggleCell } from './FeatureToggleSwitch/createFeatureToggleCell'; | ||||
| import { useFeatureToggleSwitch } from './FeatureToggleSwitch/useFeatureToggleSwitch'; | ||||
| import useLoading from 'hooks/useLoading'; | ||||
| import { DEFAULT_PAGE_LIMIT } from '../ProjectOverview'; | ||||
| 
 | ||||
| const StyledResponsiveButton = styled(ResponsiveButton)(() => ({ | ||||
|     whiteSpace: 'nowrap', | ||||
| })); | ||||
| 
 | ||||
| export interface ISortingRules { | ||||
|     sortBy: string; | ||||
|     sortOrder: 'asc' | 'desc'; | ||||
|     isFavoritesPinned: boolean; | ||||
| } | ||||
| 
 | ||||
| interface IPaginatedProjectFeatureTogglesProps { | ||||
|     features: IProject['features']; | ||||
|     environments: IProject['environments']; | ||||
| @ -78,6 +85,8 @@ interface IPaginatedProjectFeatureTogglesProps { | ||||
|     searchValue: string; | ||||
|     setSearchValue: React.Dispatch<React.SetStateAction<string>>; | ||||
|     paginationBar: JSX.Element; | ||||
|     sortingRules: ISortingRules; | ||||
|     setSortingRules: (sortingRules: ISortingRules) => void; | ||||
| } | ||||
| 
 | ||||
| const staticColumns = ['Select', 'Actions', 'name', 'favorite']; | ||||
| @ -96,6 +105,8 @@ export const PaginatedProjectFeatureToggles = ({ | ||||
|     searchValue, | ||||
|     setSearchValue, | ||||
|     paginationBar, | ||||
|     sortingRules, | ||||
|     setSortingRules, | ||||
| }: IPaginatedProjectFeatureTogglesProps) => { | ||||
|     const { classes: styles } = useStyles(); | ||||
|     const bodyLoadingRef = useLoading(loading); | ||||
| @ -223,7 +234,11 @@ export const PaginatedProjectFeatureToggles = ({ | ||||
|             { | ||||
|                 Header: 'Name', | ||||
|                 accessor: 'name', | ||||
|                 Cell: ({ value }: { value: string }) => ( | ||||
|                 Cell: ({ | ||||
|                     value, | ||||
|                 }: { | ||||
|                     value: string; | ||||
|                 }) => ( | ||||
|                     <Tooltip title={value} arrow describeChild> | ||||
|                         <span> | ||||
|                             <LinkCell | ||||
| @ -285,7 +300,7 @@ export const PaginatedProjectFeatureToggles = ({ | ||||
|                 return { | ||||
|                     Header: loading ? () => '' : name, | ||||
|                     maxWidth: 90, | ||||
|                     id: `environments.${name}`, | ||||
|                     id: `environment:${name}`, | ||||
|                     accessor: (row: ListItemType) => | ||||
|                         row.environments[name]?.enabled, | ||||
|                     align: 'center', | ||||
| @ -301,7 +316,11 @@ export const PaginatedProjectFeatureToggles = ({ | ||||
|                 id: 'Actions', | ||||
|                 maxWidth: 56, | ||||
|                 width: 56, | ||||
|                 Cell: (props: { row: { original: ListItemType } }) => ( | ||||
|                 Cell: (props: { | ||||
|                     row: { | ||||
|                         original: ListItemType; | ||||
|                     }; | ||||
|                 }) => ( | ||||
|                     <ActionsCell | ||||
|                         projectId={projectId} | ||||
|                         onOpenArchiveDialog={setFeatureArchiveState} | ||||
| @ -372,7 +391,10 @@ export const PaginatedProjectFeatureToggles = ({ | ||||
|                     name: `Feature name ${index}`, | ||||
|                     createdAt: new Date().toISOString(), | ||||
|                     environments: { | ||||
|                         production: { name: 'production', enabled: false }, | ||||
|                         production: { | ||||
|                             name: 'production', | ||||
|                             enabled: false, | ||||
|                         }, | ||||
|                     }, | ||||
|                 })); | ||||
|             // Coerce loading data to FeatureSchema[]
 | ||||
| @ -419,7 +441,7 @@ export const PaginatedProjectFeatureToggles = ({ | ||||
|                         id: | ||||
|                             searchParams.get('sort') || | ||||
|                             storedParams.id || | ||||
|                             'createdAt', | ||||
|                             sortingRules.sortBy, | ||||
|                         desc: searchParams.has('order') | ||||
|                             ? searchParams.get('order') === 'desc' | ||||
|                             : storedParams.desc, | ||||
| @ -462,10 +484,12 @@ export const PaginatedProjectFeatureToggles = ({ | ||||
|         if (loading) { | ||||
|             return; | ||||
|         } | ||||
|         const sortedByColumn = sortBy[0].id; | ||||
|         const sortOrder = sortBy[0].desc ? 'desc' : 'asc'; | ||||
|         const tableState: Record<string, string> = {}; | ||||
|         tableState.sort = sortBy[0].id; | ||||
|         tableState.sort = sortedByColumn; | ||||
|         if (sortBy[0].desc) { | ||||
|             tableState.order = 'desc'; | ||||
|             tableState.order = sortOrder; | ||||
|         } | ||||
|         if (searchValue) { | ||||
|             tableState.search = searchValue; | ||||
| @ -490,10 +514,17 @@ export const PaginatedProjectFeatureToggles = ({ | ||||
|             desc: sortBy[0].desc || false, | ||||
|             columns: tableState.columns.split(','), | ||||
|         })); | ||||
|         const favoritesPinned = Boolean(isFavoritesPinned); | ||||
|         setGlobalStore((params) => ({ | ||||
|             ...params, | ||||
|             favorites: Boolean(isFavoritesPinned), | ||||
|             favorites: favoritesPinned, | ||||
|         })); | ||||
|         setSortingRules({ | ||||
|             sortBy: sortedByColumn, | ||||
|             sortOrder, | ||||
|             isFavoritesPinned: favoritesPinned, | ||||
|         }); | ||||
| 
 | ||||
|         // eslint-disable-next-line react-hooks/exhaustive-deps
 | ||||
|     }, [ | ||||
|         loading, | ||||
| @ -504,9 +535,12 @@ export const PaginatedProjectFeatureToggles = ({ | ||||
|         isFavoritesPinned, | ||||
|     ]); | ||||
| 
 | ||||
|     const showPaginationBar = Boolean(total && total > 25); | ||||
|     const showPaginationBar = Boolean(total && total > DEFAULT_PAGE_LIMIT); | ||||
|     const style = showPaginationBar | ||||
|         ? { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 } | ||||
|         ? { | ||||
|               borderBottomLeftRadius: 0, | ||||
|               borderBottomRightRadius: 0, | ||||
|           } | ||||
|         : {}; | ||||
| 
 | ||||
|     return ( | ||||
|  | ||||
| @ -12,10 +12,14 @@ import { ProjectStats } from './ProjectStats/ProjectStats'; | ||||
| import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; | ||||
| import { useUiFlag } from 'hooks/useUiFlag'; | ||||
| import { useFeatureSearch } from 'hooks/api/getters/useFeatureSearch/useFeatureSearch'; | ||||
| import { PaginatedProjectFeatureToggles } from './ProjectFeatureToggles/PaginatedProjectFeatureToggles'; | ||||
| import { | ||||
|     ISortingRules, | ||||
|     PaginatedProjectFeatureToggles, | ||||
| } from './ProjectFeatureToggles/PaginatedProjectFeatureToggles'; | ||||
| import { useSearchParams } from 'react-router-dom'; | ||||
| 
 | ||||
| import { PaginationBar } from 'component/common/PaginationBar/PaginationBar'; | ||||
| import { SortingRule } from 'react-table'; | ||||
| 
 | ||||
| const refreshInterval = 15 * 1000; | ||||
| 
 | ||||
| @ -38,28 +42,43 @@ const StyledContentContainer = styled(Box)(() => ({ | ||||
|     minWidth: 0, | ||||
| })); | ||||
| 
 | ||||
| export const DEFAULT_PAGE_LIMIT = 25; | ||||
| 
 | ||||
| const PaginatedProjectOverview = () => { | ||||
|     const projectId = useRequiredPathParam('projectId'); | ||||
|     const [searchParams, setSearchParams] = useSearchParams(); | ||||
|     const { project, loading: projectLoading } = useProject(projectId, { | ||||
|         refreshInterval, | ||||
|     }); | ||||
|     const [pageLimit, setPageLimit] = useState(25); | ||||
|     const [pageLimit, setPageLimit] = useState(DEFAULT_PAGE_LIMIT); | ||||
|     const [currentOffset, setCurrentOffset] = useState(0); | ||||
| 
 | ||||
|     const [searchValue, setSearchValue] = useState( | ||||
|         searchParams.get('search') || '', | ||||
|     ); | ||||
| 
 | ||||
|     const [sortingRules, setSortingRules] = useState<ISortingRules>({ | ||||
|         sortBy: 'createdBy', | ||||
|         sortOrder: 'desc', | ||||
|         isFavoritesPinned: false, | ||||
|     }); | ||||
| 
 | ||||
|     const { | ||||
|         features: searchFeatures, | ||||
|         total, | ||||
|         refetch, | ||||
|         loading, | ||||
|         initialLoad, | ||||
|     } = useFeatureSearch(currentOffset, pageLimit, projectId, searchValue, { | ||||
|     } = useFeatureSearch( | ||||
|         currentOffset, | ||||
|         pageLimit, | ||||
|         sortingRules, | ||||
|         projectId, | ||||
|         searchValue, | ||||
|         { | ||||
|             refreshInterval, | ||||
|     }); | ||||
|         }, | ||||
|     ); | ||||
| 
 | ||||
|     const { members, features, health, description, environments, stats } = | ||||
|         project; | ||||
| @ -102,6 +121,8 @@ const PaginatedProjectOverview = () => { | ||||
|                         total={total} | ||||
|                         searchValue={searchValue} | ||||
|                         setSearchValue={setSearchValue} | ||||
|                         sortingRules={sortingRules} | ||||
|                         setSortingRules={setSortingRules} | ||||
|                         paginationBar={ | ||||
|                             <StickyPaginationBar> | ||||
|                                 <PaginationBar | ||||
|  | ||||
| @ -4,6 +4,7 @@ import { IFeatureToggleListItem } from 'interfaces/featureToggle'; | ||||
| import { formatApiPath } from 'utils/formatPath'; | ||||
| import handleErrorResponses from '../httpErrorResponseHandler'; | ||||
| import { translateToQueryParams } from './searchToQueryParams'; | ||||
| import { ISortingRules } from 'component/project/Project/ProjectFeatureToggles/PaginatedProjectFeatureToggles'; | ||||
| 
 | ||||
| type IFeatureSearchResponse = { | ||||
|     features: IFeatureToggleListItem[]; | ||||
| @ -62,6 +63,7 @@ const createFeatureSearch = () => { | ||||
|     return ( | ||||
|         offset: number, | ||||
|         limit: number, | ||||
|         sortingRules: ISortingRules, | ||||
|         projectId = '', | ||||
|         searchValue = '', | ||||
|         options: SWRConfiguration = {}, | ||||
| @ -71,6 +73,7 @@ const createFeatureSearch = () => { | ||||
|             offset, | ||||
|             limit, | ||||
|             searchValue, | ||||
|             sortingRules, | ||||
|         ); | ||||
| 
 | ||||
|         useEffect(() => { | ||||
| @ -113,9 +116,11 @@ const getFeatureSearchFetcher = ( | ||||
|     offset: number, | ||||
|     limit: number, | ||||
|     searchValue: string, | ||||
|     sortingRules: ISortingRules, | ||||
| ) => { | ||||
|     const searchQueryParams = translateToQueryParams(searchValue); | ||||
|     const KEY = `api/admin/search/features?projectId=${projectId}&offset=${offset}&limit=${limit}&${searchQueryParams}`; | ||||
|     const sortQueryParams = translateToSortQueryParams(sortingRules); | ||||
|     const KEY = `api/admin/search/features?projectId=${projectId}&offset=${offset}&limit=${limit}&${searchQueryParams}&${sortQueryParams}`; | ||||
|     const fetcher = () => { | ||||
|         const path = formatApiPath(KEY); | ||||
|         return fetch(path, { | ||||
| @ -130,3 +135,9 @@ const getFeatureSearchFetcher = ( | ||||
|         KEY, | ||||
|     }; | ||||
| }; | ||||
| 
 | ||||
| const translateToSortQueryParams = (sortingRules: ISortingRules) => { | ||||
|     const { sortBy, sortOrder, isFavoritesPinned } = sortingRules; | ||||
|     const sortQueryParams = `sortBy=${sortBy}&sortOrder=${sortOrder}&favoritesFirst=${isFavoritesPinned}`; | ||||
|     return sortQueryParams; | ||||
| }; | ||||
|  | ||||
| @ -713,7 +713,7 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore { | ||||
|             const [, envName] = sortBy.split(':'); | ||||
|             query = query | ||||
|                 .orderByRaw( | ||||
|                     `CASE WHEN feature_environments.environment = ? THEN feature_environments.enabled ELSE NULL END ${validatedSortOrder}`, | ||||
|                     `CASE WHEN feature_environments.environment = ? THEN feature_environments.enabled ELSE NULL END ${validatedSortOrder} NULLS LAST`, | ||||
|                     [envName], | ||||
|                 ) | ||||
|                 .orderBy('created_at', 'asc'); | ||||
| @ -733,6 +733,7 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore { | ||||
|             .select(selectColumns) | ||||
|             .limit(limit * environmentCount) | ||||
|             .offset(offset * environmentCount); | ||||
| 
 | ||||
|         const rows = await query; | ||||
| 
 | ||||
|         if (rows.length > 0) { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user