mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: improve flag filters on project page (#10705)
This commit is contained in:
		
							parent
							
								
									c12aca72db
								
							
						
					
					
						commit
						9d996f14d9
					
				| @ -37,7 +37,6 @@ interface ILifecycleFiltersBaseProps { | ||||
| const Wrapper = styled(Box)(({ theme }) => ({ | ||||
|     display: 'flex', | ||||
|     justifyContent: 'space-between', | ||||
|     minHeight: theme.spacing(7), | ||||
|     gap: theme.spacing(2), | ||||
| })); | ||||
| 
 | ||||
|  | ||||
| @ -20,7 +20,7 @@ import { | ||||
| import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled'; | ||||
| import { useFeatureToggleSwitch } from '../ProjectFeatureToggles/FeatureToggleSwitch/useFeatureToggleSwitch.tsx'; | ||||
| import useLoading from 'hooks/useLoading'; | ||||
| import { ProjectFeatureTogglesHeader } from './ProjectFeatureTogglesHeader/ProjectFeatureTogglesHeader.tsx'; | ||||
| import { ProjectFeatureTogglesHeader as LegacyProjectFeatureTogglesHeader } from './ProjectFeatureTogglesHeader/LegacyProjectFeatureTogglesHeader.tsx'; | ||||
| import { createColumnHelper, useReactTable } from '@tanstack/react-table'; | ||||
| import { withTableState } from 'utils/withTableState'; | ||||
| import type { FeatureSearchResponseSchema } from 'openapi'; | ||||
| @ -41,7 +41,7 @@ import { | ||||
|     useProjectFeatureSearchActions, | ||||
| } from './useProjectFeatureSearch.ts'; | ||||
| import { AvatarCell } from './AvatarCell.tsx'; | ||||
| import { styled } from '@mui/material'; | ||||
| import { styled, useMediaQuery, useTheme } from '@mui/material'; | ||||
| import useProjectOverview from 'hooks/api/getters/useProjectOverview/useProjectOverview'; | ||||
| import { ConnectSdkDialog } from '../../../onboarding/dialog/ConnectSdkDialog.tsx'; | ||||
| import { ProjectOnboarding } from '../../../onboarding/flow/ProjectOnboarding.tsx'; | ||||
| @ -57,6 +57,9 @@ import { IMPORT_BUTTON } from 'utils/testIds'; | ||||
| import { ProjectCleanupReminder } from './ProjectCleanupReminder/ProjectCleanupReminder.tsx'; | ||||
| import { formatEnvironmentColumnId } from './formatEnvironmentColumnId.ts'; | ||||
| import { ProjectFeaturesColumnsMenu } from './ProjectFeaturesColumnsMenu/ProjectFeaturesColumnsMenu.tsx'; | ||||
| import { useUiFlag } from 'hooks/useUiFlag.ts'; | ||||
| import { ProjectFeatureTogglesHeader } from './ProjectFeatureTogglesHeader/ProjectFeatureTogglesHeader.tsx'; | ||||
| import { ProjectFlagsSearch } from './ProjectFlagsSearch/ProjectFlagsSearch.tsx'; | ||||
| 
 | ||||
| type ProjectFeatureTogglesProps = { | ||||
|     environments: string[]; | ||||
| @ -71,12 +74,27 @@ const Container = styled('div')(({ theme }) => ({ | ||||
|     gap: theme.spacing(2), | ||||
| })); | ||||
| 
 | ||||
| const FilterRow = styled('div')(({ theme }) => ({ | ||||
| const LegacyFilterRow = styled('div')(({ theme }) => ({ | ||||
|     display: 'flex', | ||||
|     flexFlow: 'row wrap', | ||||
|     justifyContent: 'space-between', | ||||
| })); | ||||
| 
 | ||||
| const FiltersContainer = styled('div')(({ theme }) => ({ | ||||
|     display: 'flex', | ||||
|     flexDirection: 'column', | ||||
|     gap: theme.spacing(1), | ||||
|     padding: theme.spacing(2, 3, 2), | ||||
|     [theme.breakpoints.down('md')]: { | ||||
|         padding: theme.spacing(2, 2), | ||||
|     }, | ||||
| })); | ||||
| 
 | ||||
| const FilterRow = styled('div')({ | ||||
|     display: 'flex', | ||||
|     alignItems: 'center', | ||||
| }); | ||||
| 
 | ||||
| const ButtonGroup = styled('div')(({ theme }) => ({ | ||||
|     display: 'flex', | ||||
|     gap: theme.spacing(1), | ||||
| @ -91,6 +109,9 @@ export const ProjectFeatureToggles = ({ | ||||
|     const { project } = useProjectOverview(projectId); | ||||
|     const [connectSdkOpen, setConnectSdkOpen] = useState(false); | ||||
|     const [modalOpen, setModalOpen] = useState(false); | ||||
|     const flagsUiFilterRefactorEnabled = useUiFlag('flagsUiFilterRefactor'); | ||||
|     const theme = useTheme(); | ||||
|     const isSmallScreen = useMediaQuery(theme.breakpoints.down('md')); | ||||
| 
 | ||||
|     const { | ||||
|         features, | ||||
| @ -500,25 +521,32 @@ export const ProjectFeatureToggles = ({ | ||||
|             ) : null} | ||||
|             <PageContent | ||||
|                 disableLoading | ||||
|                 disablePadding | ||||
|                 header={ | ||||
|                     <ProjectFeatureTogglesHeader | ||||
|                         isLoading={initialLoad} | ||||
|                         totalItems={total} | ||||
|                         searchQuery={tableState.query || ''} | ||||
|                         onChangeSearchQuery={(query) => { | ||||
|                             setTableState({ query }); | ||||
|                         }} | ||||
|                         dataToExport={data} | ||||
|                         environmentsToExport={environments} | ||||
|                         actions={ | ||||
|                             <ProjectFeaturesColumnsMenu | ||||
|                                 columnVisibility={columnVisibility} | ||||
|                                 environments={environments} | ||||
|                                 onToggle={onToggleColumnVisibility} | ||||
|                             /> | ||||
|                         } | ||||
|                     /> | ||||
|                     flagsUiFilterRefactorEnabled ? ( | ||||
|                         <ProjectFeatureTogglesHeader | ||||
|                             isLoading={initialLoad} | ||||
|                             totalItems={total} | ||||
|                             environmentsToExport={environments} | ||||
|                         /> | ||||
|                     ) : ( | ||||
|                         <LegacyProjectFeatureTogglesHeader | ||||
|                             isLoading={initialLoad} | ||||
|                             totalItems={total} | ||||
|                             searchQuery={tableState.query || ''} | ||||
|                             onChangeSearchQuery={(query) => { | ||||
|                                 setTableState({ query }); | ||||
|                             }} | ||||
|                             dataToExport={data} | ||||
|                             environmentsToExport={environments} | ||||
|                             actions={ | ||||
|                                 <ProjectFeaturesColumnsMenu | ||||
|                                     columnVisibility={columnVisibility} | ||||
|                                     environments={environments} | ||||
|                                     onToggle={onToggleColumnVisibility} | ||||
|                                 /> | ||||
|                             } | ||||
|                         /> | ||||
|                     ) | ||||
|                 } | ||||
|                 bodyClass='noop' | ||||
|                 style={{ cursor: 'inherit' }} | ||||
| @ -528,31 +556,74 @@ export const ProjectFeatureToggles = ({ | ||||
|                     aria-busy={isPlaceholder} | ||||
|                     aria-live='polite' | ||||
|                 > | ||||
|                     <FilterRow> | ||||
|                         <ProjectOverviewFilters | ||||
|                             project={projectId} | ||||
|                             onChange={setTableState} | ||||
|                             state={filterState} | ||||
|                         /> | ||||
|                         <ProjectLifecycleFilters | ||||
|                             projectId={projectId} | ||||
|                             state={filterState} | ||||
|                             onChange={setTableState} | ||||
|                             total={loading ? undefined : total} | ||||
|                         /> | ||||
|                         <ButtonGroup> | ||||
|                             <PermissionIconButton | ||||
|                                 permission={UPDATE_FEATURE} | ||||
|                     {flagsUiFilterRefactorEnabled ? ( | ||||
|                         <FiltersContainer> | ||||
|                             <FilterRow> | ||||
|                                 <ProjectLifecycleFilters | ||||
|                                     projectId={projectId} | ||||
|                                     state={filterState} | ||||
|                                     onChange={setTableState} | ||||
|                                     total={loading ? undefined : total} | ||||
|                                 /> | ||||
|                                 {isSmallScreen ? null : ( | ||||
|                                     <ProjectFlagsSearch | ||||
|                                         searchQuery={tableState.query || ''} | ||||
|                                         onChangeSearchQuery={(query) => { | ||||
|                                             setTableState({ query }); | ||||
|                                         }} | ||||
|                                         isLoading={loading} | ||||
|                                     /> | ||||
|                                 )} | ||||
|                                 <ProjectFeaturesColumnsMenu | ||||
|                                     columnVisibility={columnVisibility} | ||||
|                                     environments={environments} | ||||
|                                     onToggle={onToggleColumnVisibility} | ||||
|                                 /> | ||||
|                             </FilterRow> | ||||
|                             <FilterRow> | ||||
|                                 <ProjectOverviewFilters | ||||
|                                     project={projectId} | ||||
|                                     onChange={setTableState} | ||||
|                                     state={filterState} | ||||
|                                 /> | ||||
|                             </FilterRow> | ||||
|                             {isSmallScreen ? ( | ||||
|                                 <ProjectFlagsSearch | ||||
|                                     searchQuery={tableState.query || ''} | ||||
|                                     onChangeSearchQuery={(query) => { | ||||
|                                         setTableState({ query }); | ||||
|                                     }} | ||||
|                                     isLoading={loading} | ||||
|                                 /> | ||||
|                             ) : null} | ||||
|                         </FiltersContainer> | ||||
|                     ) : ( | ||||
|                         <LegacyFilterRow> | ||||
|                             <ProjectOverviewFilters | ||||
|                                 project={projectId} | ||||
|                                 onChange={setTableState} | ||||
|                                 state={filterState} | ||||
|                             /> | ||||
|                             <ProjectLifecycleFilters | ||||
|                                 projectId={projectId} | ||||
|                                 onClick={() => setModalOpen(true)} | ||||
|                                 tooltipProps={{ title: 'Import' }} | ||||
|                                 data-testid={IMPORT_BUTTON} | ||||
|                                 data-loading-project | ||||
|                             > | ||||
|                                 <ImportSvg /> | ||||
|                             </PermissionIconButton> | ||||
|                         </ButtonGroup> | ||||
|                     </FilterRow> | ||||
|                                 state={filterState} | ||||
|                                 onChange={setTableState} | ||||
|                                 total={loading ? undefined : total} | ||||
|                             /> | ||||
|                             <ButtonGroup> | ||||
|                                 <PermissionIconButton | ||||
|                                     permission={UPDATE_FEATURE} | ||||
|                                     projectId={projectId} | ||||
|                                     onClick={() => setModalOpen(true)} | ||||
|                                     tooltipProps={{ title: 'Import' }} | ||||
|                                     data-testid={IMPORT_BUTTON} | ||||
|                                     data-loading-project | ||||
|                                 > | ||||
|                                     <ImportSvg /> | ||||
|                                 </PermissionIconButton> | ||||
|                             </ButtonGroup> | ||||
|                         </LegacyFilterRow> | ||||
|                     )} | ||||
|                     <SearchHighlightProvider value={tableState.query || ''}> | ||||
|                         <PaginatedTable | ||||
|                             tableInstance={table} | ||||
|  | ||||
| @ -0,0 +1,165 @@ | ||||
| import { type ReactNode, type FC, useState } from 'react'; | ||||
| import { | ||||
|     Box, | ||||
|     Button, | ||||
|     IconButton, | ||||
|     Tooltip, | ||||
|     useMediaQuery, | ||||
|     useTheme, | ||||
| } from '@mui/material'; | ||||
| import useLoading from 'hooks/useLoading'; | ||||
| import { PageHeader } from 'component/common/PageHeader/PageHeader'; | ||||
| import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; | ||||
| import { Search } from 'component/common/Search/Search'; | ||||
| import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; | ||||
| import { ExportDialog } from 'component/feature/FeatureToggleList/ExportDialog'; | ||||
| import type { FeatureSchema } from 'openapi'; | ||||
| import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; | ||||
| import ReviewsOutlined from '@mui/icons-material/ReviewsOutlined'; | ||||
| import { useFeedback } from 'component/feedbackNew/useFeedback'; | ||||
| import IosShare from '@mui/icons-material/IosShare'; | ||||
| import { FlagCreationButton } from './FlagCreationButton/FlagCreationButton.tsx'; | ||||
| 
 | ||||
| interface IProjectFeatureTogglesHeaderProps { | ||||
|     isLoading?: boolean; | ||||
|     totalItems?: number; | ||||
|     searchQuery?: string; | ||||
|     onChangeSearchQuery?: (query: string) => void; | ||||
|     dataToExport?: Pick<FeatureSchema, 'name'>[]; | ||||
|     environmentsToExport?: string[]; | ||||
|     actions?: ReactNode; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * @deprecated remove with `flagsUiFilterRefactor` flag | ||||
|  */ | ||||
| export const ProjectFeatureTogglesHeader: FC< | ||||
|     IProjectFeatureTogglesHeaderProps | ||||
| > = ({ | ||||
|     isLoading, | ||||
|     totalItems, | ||||
|     searchQuery, | ||||
|     onChangeSearchQuery, | ||||
|     environmentsToExport, | ||||
|     actions, | ||||
| }) => { | ||||
|     const projectId = useRequiredPathParam('projectId'); | ||||
|     const headerLoadingRef = useLoading(isLoading || false); | ||||
|     const [showTitle, setShowTitle] = useState(true); | ||||
|     const theme = useTheme(); | ||||
|     const isSmallScreen = useMediaQuery(theme.breakpoints.down('md')); | ||||
|     const [showExportDialog, setShowExportDialog] = useState(false); | ||||
|     const { trackEvent } = usePlausibleTracker(); | ||||
|     const projectOverviewRefactorFeedback = false; | ||||
|     const { openFeedback } = useFeedback('newProjectOverview', 'automatic'); | ||||
|     const handleSearch = (query: string) => { | ||||
|         onChangeSearchQuery?.(query); | ||||
|         trackEvent('search-bar', { | ||||
|             props: { | ||||
|                 screen: 'project', | ||||
|                 length: query.length, | ||||
|             }, | ||||
|         }); | ||||
|     }; | ||||
| 
 | ||||
|     const createFeedbackContext = () => { | ||||
|         openFeedback({ | ||||
|             title: 'How easy was it to work with the project overview in Unleash?', | ||||
|             positiveLabel: | ||||
|                 'What do you like most about the updated project overview?', | ||||
|             areasForImprovementsLabel: | ||||
|                 'What improvements are needed in the project overview?', | ||||
|         }); | ||||
|     }; | ||||
| 
 | ||||
|     return ( | ||||
|         <Box ref={headerLoadingRef} aria-busy={isLoading} aria-live='polite'> | ||||
|             <PageHeader | ||||
|                 titleElement={ | ||||
|                     showTitle | ||||
|                         ? `Feature flags ${ | ||||
|                               totalItems !== undefined ? `(${totalItems})` : '' | ||||
|                           }` | ||||
|                         : null | ||||
|                 } | ||||
|                 actions={ | ||||
|                     <> | ||||
|                         <ConditionallyRender | ||||
|                             condition={!isSmallScreen} | ||||
|                             show={ | ||||
|                                 <Search | ||||
|                                     data-loading | ||||
|                                     placeholder='Search and Filter' | ||||
|                                     expandable | ||||
|                                     initialValue={searchQuery || ''} | ||||
|                                     onChange={handleSearch} | ||||
|                                     onFocus={() => setShowTitle(false)} | ||||
|                                     onBlur={() => setShowTitle(true)} | ||||
|                                     hasFilters | ||||
|                                     id='projectFeatureFlags' | ||||
|                                 /> | ||||
|                             } | ||||
|                         /> | ||||
|                         {actions} | ||||
|                         <PageHeader.Divider sx={{ marginLeft: 0 }} /> | ||||
|                         <Tooltip title='Export all project flags' arrow> | ||||
|                             <IconButton | ||||
|                                 data-loading | ||||
|                                 onClick={() => setShowExportDialog(true)} | ||||
|                                 sx={(theme) => ({ | ||||
|                                     marginRight: theme.spacing(2), | ||||
|                                 })} | ||||
|                             > | ||||
|                                 <IosShare /> | ||||
|                             </IconButton> | ||||
|                         </Tooltip> | ||||
| 
 | ||||
|                         <ConditionallyRender | ||||
|                             condition={!isLoading} | ||||
|                             show={ | ||||
|                                 <ExportDialog | ||||
|                                     showExportDialog={showExportDialog} | ||||
|                                     project={projectId} | ||||
|                                     data={[]} | ||||
|                                     onClose={() => setShowExportDialog(false)} | ||||
|                                     environments={environmentsToExport || []} | ||||
|                                 /> | ||||
|                             } | ||||
|                         /> | ||||
| 
 | ||||
|                         {/* FIXME: remove */} | ||||
|                         <ConditionallyRender | ||||
|                             condition={ | ||||
|                                 projectOverviewRefactorFeedback && | ||||
|                                 !isSmallScreen | ||||
|                             } | ||||
|                             show={ | ||||
|                                 <Button | ||||
|                                     startIcon={<ReviewsOutlined />} | ||||
|                                     onClick={createFeedbackContext} | ||||
|                                     variant='outlined' | ||||
|                                     data-loading | ||||
|                                 > | ||||
|                                     Provide feedback | ||||
|                                 </Button> | ||||
|                             } | ||||
|                         /> | ||||
|                         <FlagCreationButton isLoading={isLoading} /> | ||||
|                     </> | ||||
|                 } | ||||
|             > | ||||
|                 <ConditionallyRender | ||||
|                     condition={isSmallScreen} | ||||
|                     show={ | ||||
|                         <Search | ||||
|                             initialValue={searchQuery || ''} | ||||
|                             onChange={handleSearch} | ||||
|                             hasFilters | ||||
|                             id='projectFeatureFlags' | ||||
|                         /> | ||||
|                     } | ||||
|                 /> | ||||
|             </PageHeader> | ||||
|         </Box> | ||||
|     ); | ||||
| }; | ||||
| @ -1,136 +1,59 @@ | ||||
| import { type ReactNode, type FC, useState } from 'react'; | ||||
| import { | ||||
|     Box, | ||||
|     IconButton, | ||||
|     Tooltip, | ||||
|     useMediaQuery, | ||||
|     useTheme, | ||||
| } from '@mui/material'; | ||||
| import { type FC, useState } from 'react'; | ||||
| import { Box, IconButton, Tooltip } from '@mui/material'; | ||||
| import useLoading from 'hooks/useLoading'; | ||||
| import { PageHeader } from 'component/common/PageHeader/PageHeader'; | ||||
| import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; | ||||
| import { Search } from 'component/common/Search/Search'; | ||||
| import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; | ||||
| import { ExportDialog } from 'component/feature/FeatureToggleList/ExportDialog'; | ||||
| import type { FeatureSchema } from 'openapi'; | ||||
| import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; | ||||
| import IosShare from '@mui/icons-material/IosShare'; | ||||
| import { FlagCreationButton } from './FlagCreationButton/FlagCreationButton.tsx'; | ||||
| import { ImportButton } from './ImportButton/ImportButton.tsx'; | ||||
| 
 | ||||
| interface IProjectFeatureTogglesHeaderProps { | ||||
| type ProjectFeatureTogglesHeaderProps = { | ||||
|     isLoading?: boolean; | ||||
|     totalItems?: number; | ||||
|     searchQuery?: string; | ||||
|     onChangeSearchQuery?: (query: string) => void; | ||||
|     dataToExport?: Pick<FeatureSchema, 'name'>[]; | ||||
|     environmentsToExport?: string[]; | ||||
|     actions?: ReactNode; | ||||
| } | ||||
| }; | ||||
| 
 | ||||
| export const ProjectFeatureTogglesHeader: FC< | ||||
|     IProjectFeatureTogglesHeaderProps | ||||
| > = ({ | ||||
|     isLoading, | ||||
|     totalItems, | ||||
|     searchQuery, | ||||
|     onChangeSearchQuery, | ||||
|     environmentsToExport, | ||||
|     actions, | ||||
| }) => { | ||||
|     ProjectFeatureTogglesHeaderProps | ||||
| > = ({ isLoading, totalItems, environmentsToExport }) => { | ||||
|     const projectId = useRequiredPathParam('projectId'); | ||||
|     const headerLoadingRef = useLoading(isLoading || false); | ||||
|     const [showTitle, setShowTitle] = useState(true); | ||||
|     const theme = useTheme(); | ||||
|     const isSmallScreen = useMediaQuery(theme.breakpoints.down('md')); | ||||
|     const [showExportDialog, setShowExportDialog] = useState(false); | ||||
|     const { trackEvent } = usePlausibleTracker(); | ||||
|     const handleSearch = (query: string) => { | ||||
|         onChangeSearchQuery?.(query); | ||||
|         trackEvent('search-bar', { | ||||
|             props: { | ||||
|                 screen: 'project', | ||||
|                 length: query.length, | ||||
|             }, | ||||
|         }); | ||||
|     }; | ||||
| 
 | ||||
|     return ( | ||||
|         <Box | ||||
|             ref={headerLoadingRef} | ||||
|             aria-busy={isLoading} | ||||
|             aria-live='polite' | ||||
|             sx={(theme) => ({ | ||||
|                 padding: `${theme.spacing(2.5)} ${theme.spacing(3.125)}`, | ||||
|             })} | ||||
|         > | ||||
|         <Box ref={headerLoadingRef} aria-busy={isLoading} aria-live='polite'> | ||||
|             <PageHeader | ||||
|                 titleElement={ | ||||
|                     showTitle | ||||
|                         ? `Feature flags ${ | ||||
|                               totalItems !== undefined ? `(${totalItems})` : '' | ||||
|                           }` | ||||
|                         : null | ||||
|                 } | ||||
|                 titleElement={`Feature flags ${ | ||||
|                     totalItems !== undefined ? `(${totalItems})` : '' | ||||
|                 }`}
 | ||||
|                 actions={ | ||||
|                     <> | ||||
|                         <ConditionallyRender | ||||
|                             condition={!isSmallScreen} | ||||
|                             show={ | ||||
|                                 <Search | ||||
|                                     data-loading | ||||
|                                     placeholder='Search and Filter' | ||||
|                                     expandable | ||||
|                                     initialValue={searchQuery || ''} | ||||
|                                     onChange={handleSearch} | ||||
|                                     onFocus={() => setShowTitle(false)} | ||||
|                                     onBlur={() => setShowTitle(true)} | ||||
|                                     hasFilters | ||||
|                                     id='projectFeatureFlags' | ||||
|                                 /> | ||||
|                             } | ||||
|                         /> | ||||
|                         {actions} | ||||
|                         <PageHeader.Divider sx={{ marginLeft: 0 }} /> | ||||
|                         <Tooltip title='Export all project flags' arrow> | ||||
|                             <IconButton | ||||
|                                 data-loading | ||||
|                                 onClick={() => setShowExportDialog(true)} | ||||
|                                 sx={(theme) => ({ | ||||
|                                     marginRight: theme.spacing(2), | ||||
|                                 })} | ||||
|                             > | ||||
|                                 <IosShare /> | ||||
|                             </IconButton> | ||||
|                         </Tooltip> | ||||
|                         <ImportButton /> | ||||
| 
 | ||||
|                         <ConditionallyRender | ||||
|                             condition={!isLoading} | ||||
|                             show={ | ||||
|                                 <ExportDialog | ||||
|                                     showExportDialog={showExportDialog} | ||||
|                                     project={projectId} | ||||
|                                     data={[]} | ||||
|                                     onClose={() => setShowExportDialog(false)} | ||||
|                                     environments={environmentsToExport || []} | ||||
|                                 /> | ||||
|                             } | ||||
|                         /> | ||||
|                         <FlagCreationButton isLoading={isLoading} /> | ||||
|                         {!isLoading ? ( | ||||
|                             <ExportDialog | ||||
|                                 showExportDialog={showExportDialog} | ||||
|                                 project={projectId} | ||||
|                                 data={[]} | ||||
|                                 onClose={() => setShowExportDialog(false)} | ||||
|                                 environments={environmentsToExport || []} | ||||
|                             /> | ||||
|                         ) : null} | ||||
|                         <Box> | ||||
|                             <FlagCreationButton isLoading={isLoading} /> | ||||
|                         </Box> | ||||
|                     </> | ||||
|                 } | ||||
|             > | ||||
|                 <ConditionallyRender | ||||
|                     condition={isSmallScreen} | ||||
|                     show={ | ||||
|                         <Search | ||||
|                             initialValue={searchQuery || ''} | ||||
|                             onChange={handleSearch} | ||||
|                             hasFilters | ||||
|                             id='projectFeatureFlags' | ||||
|                         /> | ||||
|                     } | ||||
|                 /> | ||||
|             </PageHeader> | ||||
|             /> | ||||
|         </Box> | ||||
|     ); | ||||
| }; | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| import type { FC } from 'react'; | ||||
| import { Box } from '@mui/material'; | ||||
| import { ColumnsMenu } from '../ColumnsMenu/ColumnsMenu.tsx'; | ||||
| import { formatEnvironmentColumnId } from '../formatEnvironmentColumnId.ts'; | ||||
| 
 | ||||
| @ -12,47 +13,49 @@ export const ProjectFeaturesColumnsMenu: FC< | ||||
|     ProjectFeaturesColumnsMenuProps | ||||
| > = ({ columnVisibility, environments, onToggle }) => { | ||||
|     return ( | ||||
|         <ColumnsMenu | ||||
|             columns={[ | ||||
|                 { | ||||
|                     header: 'Name', | ||||
|                     id: 'name', | ||||
|                     isVisible: columnVisibility.name, | ||||
|                     isStatic: true, | ||||
|                 }, | ||||
|                 { | ||||
|                     header: 'Created', | ||||
|                     id: 'createdAt', | ||||
|                     isVisible: columnVisibility.createdAt, | ||||
|                 }, | ||||
|                 { | ||||
|                     header: 'By', | ||||
|                     id: 'createdBy', | ||||
|                     isVisible: columnVisibility.createdBy, | ||||
|                 }, | ||||
|                 { | ||||
|                     header: 'Last seen', | ||||
|                     id: 'lastSeenAt', | ||||
|                     isVisible: columnVisibility.lastSeenAt, | ||||
|                 }, | ||||
|                 { | ||||
|                     header: 'Lifecycle', | ||||
|                     id: 'lifecycle', | ||||
|                     isVisible: columnVisibility.lifecycle, | ||||
|                 }, | ||||
|                 { | ||||
|                     id: 'divider', | ||||
|                 }, | ||||
|                 ...environments.map((environment) => ({ | ||||
|                     header: environment, | ||||
|                     id: formatEnvironmentColumnId(environment), | ||||
|                     isVisible: | ||||
|                         columnVisibility[ | ||||
|                             formatEnvironmentColumnId(environment) | ||||
|                         ], | ||||
|                 })), | ||||
|             ]} | ||||
|             onToggle={onToggle} | ||||
|         /> | ||||
|         <Box sx={(theme) => ({ marginLeft: theme.spacing(1) })}> | ||||
|             <ColumnsMenu | ||||
|                 columns={[ | ||||
|                     { | ||||
|                         header: 'Name', | ||||
|                         id: 'name', | ||||
|                         isVisible: columnVisibility.name, | ||||
|                         isStatic: true, | ||||
|                     }, | ||||
|                     { | ||||
|                         header: 'Created', | ||||
|                         id: 'createdAt', | ||||
|                         isVisible: columnVisibility.createdAt, | ||||
|                     }, | ||||
|                     { | ||||
|                         header: 'By', | ||||
|                         id: 'createdBy', | ||||
|                         isVisible: columnVisibility.createdBy, | ||||
|                     }, | ||||
|                     { | ||||
|                         header: 'Last seen', | ||||
|                         id: 'lastSeenAt', | ||||
|                         isVisible: columnVisibility.lastSeenAt, | ||||
|                     }, | ||||
|                     { | ||||
|                         header: 'Lifecycle', | ||||
|                         id: 'lifecycle', | ||||
|                         isVisible: columnVisibility.lifecycle, | ||||
|                     }, | ||||
|                     { | ||||
|                         id: 'divider', | ||||
|                     }, | ||||
|                     ...environments.map((environment) => ({ | ||||
|                         header: environment, | ||||
|                         id: formatEnvironmentColumnId(environment), | ||||
|                         isVisible: | ||||
|                             columnVisibility[ | ||||
|                                 formatEnvironmentColumnId(environment) | ||||
|                             ], | ||||
|                     })), | ||||
|                 ]} | ||||
|                 onToggle={onToggle} | ||||
|             /> | ||||
|         </Box> | ||||
|     ); | ||||
| }; | ||||
|  | ||||
| @ -4,6 +4,7 @@ import type { FilterItemParamHolder } from '../../../filter/Filters/Filters.tsx' | ||||
| import { useProjectStatus } from 'hooks/api/getters/useProjectStatus/useProjectStatus'; | ||||
| import { LifecycleFilters } from 'component/common/LifecycleFilters/LifecycleFilters.tsx'; | ||||
| import { Box, useMediaQuery, useTheme } from '@mui/material'; | ||||
| import { useUiFlag } from 'hooks/useUiFlag.ts'; | ||||
| 
 | ||||
| type ProjectLifecycleFiltersProps = { | ||||
|     projectId: string; | ||||
| @ -23,6 +24,7 @@ export const ProjectLifecycleFilters: FC<ProjectLifecycleFiltersProps> = ({ | ||||
|     const { data } = useProjectStatus(projectId); | ||||
|     const theme = useTheme(); | ||||
|     const isSmallScreen = useMediaQuery(theme.breakpoints.down('md')); | ||||
|     const flagsUiFilterRefactorEnabled = useUiFlag('flagsUiFilterRefactor'); | ||||
|     const lifecycleSummary = Object.entries( | ||||
|         data?.lifecycleSummary || {}, | ||||
|     ).reduce( | ||||
| @ -48,7 +50,13 @@ export const ProjectLifecycleFilters: FC<ProjectLifecycleFiltersProps> = ({ | ||||
|         <Box | ||||
|             sx={{ | ||||
|                 marginRight: 'auto', | ||||
|                 margin: isSmallScreen ? theme.spacing(0, 2) : '0 auto 0 0', | ||||
|                 ...(!flagsUiFilterRefactorEnabled | ||||
|                     ? { | ||||
|                           margin: isSmallScreen | ||||
|                               ? theme.spacing(0, 3) | ||||
|                               : `${theme.spacing(1.5)} auto 0 0`, | ||||
|                       } | ||||
|                     : {}), | ||||
|             }} | ||||
|         > | ||||
|             <LifecycleFilters | ||||
|  | ||||
| @ -7,6 +7,8 @@ import { | ||||
| } from 'component/filter/Filters/Filters'; | ||||
| import { useProjectFlagCreators } from 'hooks/api/getters/useProjectFlagCreators/useProjectFlagCreators'; | ||||
| import { formatTag } from 'utils/format-tag'; | ||||
| import { styled } from '@mui/material'; | ||||
| import { useUiFlag } from 'hooks/useUiFlag'; | ||||
| 
 | ||||
| type ProjectOverviewFiltersProps = { | ||||
|     state: FilterItemParamHolder; | ||||
| @ -14,6 +16,10 @@ type ProjectOverviewFiltersProps = { | ||||
|     project: string; | ||||
| }; | ||||
| 
 | ||||
| const StyledFilters = styled(Filters)({ | ||||
|     padding: 0, | ||||
| }); | ||||
| 
 | ||||
| export const ProjectOverviewFilters: FC<ProjectOverviewFiltersProps> = ({ | ||||
|     state, | ||||
|     onChange, | ||||
| @ -22,6 +28,10 @@ export const ProjectOverviewFilters: FC<ProjectOverviewFiltersProps> = ({ | ||||
|     const { tags } = useAllTags(); | ||||
|     const { flagCreators } = useProjectFlagCreators(project); | ||||
|     const [availableFilters, setAvailableFilters] = useState<IFilterItem[]>([]); | ||||
|     const flagsUiFilterRefactorEnabled = useUiFlag('flagsUiFilterRefactor'); | ||||
|     const FilterComponent = flagsUiFilterRefactorEnabled | ||||
|         ? StyledFilters | ||||
|         : Filters; | ||||
| 
 | ||||
|     useEffect(() => { | ||||
|         const tagsOptions = (tags || []).map((tag) => { | ||||
| @ -124,7 +134,7 @@ export const ProjectOverviewFilters: FC<ProjectOverviewFiltersProps> = ({ | ||||
|     }, [JSON.stringify(tags), JSON.stringify(flagCreators)]); | ||||
| 
 | ||||
|     return ( | ||||
|         <Filters | ||||
|         <FilterComponent | ||||
|             availableFilters={availableFilters} | ||||
|             state={state} | ||||
|             onChange={onChange} | ||||
|  | ||||
| @ -88,6 +88,7 @@ export type UiFlags = { | ||||
|     lifecycleGraphs?: boolean; | ||||
|     newStrategyModal?: boolean; | ||||
|     globalChangeRequestList?: boolean; | ||||
|     flagsUiFilterRefactor?: boolean; | ||||
| }; | ||||
| 
 | ||||
| export interface IVersionInfo { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user