diff --git a/frontend/src/component/project/Project/ExperimentalProjectFeatures/ExperimentalProjectFeatures.tsx b/frontend/src/component/project/Project/ExperimentalProjectFeatures/ExperimentalProjectFeatures.tsx new file mode 100644 index 0000000000..11ee148b3d --- /dev/null +++ b/frontend/src/component/project/Project/ExperimentalProjectFeatures/ExperimentalProjectFeatures.tsx @@ -0,0 +1,202 @@ +import React, { useEffect, useState } from 'react'; +import useProject, { + useProjectNameOrId, +} from 'hooks/api/getters/useProject/useProject'; +import { Box, styled } from '@mui/material'; +import { ProjectFeatureToggles } from '../ProjectFeatureToggles/ProjectFeatureToggles'; +import { usePageTitle } from 'hooks/usePageTitle'; +import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; +import { useLastViewedProject } from 'hooks/useLastViewedProject'; + +import { useUiFlag } from 'hooks/useUiFlag'; +import { useFeatureSearch } from 'hooks/api/getters/useFeatureSearch/useFeatureSearch'; +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; + +const StyledContainer = styled('div')(({ theme }) => ({ + display: 'flex', + [theme.breakpoints.down('md')]: { + flexDirection: 'column', + }, +})); + +const StyledProjectToggles = styled('div')(() => ({ + width: '100%', + minWidth: 0, +})); + +const StyledContentContainer = styled(Box)(() => ({ + display: 'flex', + flexDirection: 'column', + width: '100%', + 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(DEFAULT_PAGE_LIMIT); + const [currentOffset, setCurrentOffset] = useState(0); + + const [searchValue, setSearchValue] = useState( + searchParams.get('search') || '', + ); + + const [sortingRules, setSortingRules] = useState({ + sortBy: 'createdBy', + sortOrder: 'desc', + isFavoritesPinned: false, + }); + + const { + features: searchFeatures, + total, + refetch, + loading, + initialLoad, + } = useFeatureSearch( + currentOffset, + pageLimit, + sortingRules, + projectId, + searchValue, + { + refreshInterval, + }, + ); + + const { environments } = project; + const fetchNextPage = () => { + if (!loading) { + setCurrentOffset(Math.min(total, currentOffset + pageLimit)); + } + }; + const fetchPrevPage = () => { + setCurrentOffset(Math.max(0, currentOffset - pageLimit)); + }; + + const hasPreviousPage = currentOffset > 0; + const hasNextPage = currentOffset + pageLimit < total; + + return ( + + + + + + + } + /> + + + + ); +}; + +const StyledStickyBar = styled('div')(({ theme }) => ({ + position: 'sticky', + bottom: 0, + backgroundColor: theme.palette.background.paper, + padding: theme.spacing(2), + zIndex: 9999, + borderBottomLeftRadius: theme.shape.borderRadiusMedium, + borderBottomRightRadius: theme.shape.borderRadiusMedium, + borderTop: `1px solid ${theme.palette.divider}`, + boxShadow: `0px -2px 8px 0px rgba(32, 32, 33, 0.06)`, + height: '52px', +})); + +const StyledStickyBarContentContainer = styled(Box)(({ theme }) => ({ + display: 'flex', + justifyContent: 'space-between', + width: '100%', + minWidth: 0, +})); + +const StickyPaginationBar: React.FC = ({ children }) => { + return ( + + + {children} + + + ); +}; + +/** + * @deprecated remove when flag `featureSearchFrontend` is removed + */ +export const ExperimentalProjectFeatures = () => { + const projectId = useRequiredPathParam('projectId'); + const projectName = useProjectNameOrId(projectId); + const { project, loading, refetch } = useProject(projectId, { + refreshInterval, + }); + const { features, environments } = project; + usePageTitle(`Project overview – ${projectName}`); + const { setLastViewed } = useLastViewedProject(); + const featureSearchFrontend = useUiFlag('featureSearchFrontend'); + + useEffect(() => { + setLastViewed(projectId); + }, [projectId, setLastViewed]); + + if (featureSearchFrontend) return ; + + return ( + + + + + + + + ); +}; diff --git a/frontend/src/component/project/Project/Project.tsx b/frontend/src/component/project/Project/Project.tsx index 718202199a..cf1ff921a9 100644 --- a/frontend/src/component/project/Project/Project.tsx +++ b/frontend/src/component/project/Project/Project.tsx @@ -40,6 +40,7 @@ import { EnterpriseBadge } from 'component/common/EnterpriseBadge/EnterpriseBadg import { Badge } from 'component/common/Badge/Badge'; import { ProjectDoraMetrics } from './ProjectDoraMetrics/ProjectDoraMetrics'; import { UiFlags } from 'interfaces/uiConfig'; +import { ExperimentalProjectFeatures } from './ExperimentalProjectFeatures/ExperimentalProjectFeatures'; const StyledBadge = styled(Badge)(({ theme }) => ({ position: 'absolute', @@ -283,6 +284,10 @@ export const Project = () => { } /> } /> } /> + } + /> } diff --git a/frontend/src/component/project/Project/ProjectFeatureToggles/PaginatedProjectFeatureToggles.tsx b/frontend/src/component/project/Project/ProjectFeatureToggles/PaginatedProjectFeatureToggles.tsx index a0c28ca99f..ce2621c0f2 100644 --- a/frontend/src/component/project/Project/ProjectFeatureToggles/PaginatedProjectFeatureToggles.tsx +++ b/frontend/src/component/project/Project/ProjectFeatureToggles/PaginatedProjectFeatureToggles.tsx @@ -87,6 +87,7 @@ interface IPaginatedProjectFeatureTogglesProps { paginationBar: JSX.Element; sortingRules: ISortingRules; setSortingRules: (sortingRules: ISortingRules) => void; + style?: React.CSSProperties; } const staticColumns = ['Select', 'Actions', 'name', 'favorite']; @@ -107,6 +108,7 @@ export const PaginatedProjectFeatureToggles = ({ paginationBar, sortingRules, setSortingRules, + style = {}, }: IPaginatedProjectFeatureTogglesProps) => { const { classes: styles } = useStyles(); const bodyLoadingRef = useLoading(loading); @@ -536,7 +538,7 @@ export const PaginatedProjectFeatureToggles = ({ ]); const showPaginationBar = Boolean(total && total > DEFAULT_PAGE_LIMIT); - const style = showPaginationBar + const paginatedStyles = showPaginationBar ? { borderBottomLeftRadius: 0, borderBottomRightRadius: 0, @@ -549,7 +551,7 @@ export const PaginatedProjectFeatureToggles = ({ disableLoading disablePadding className={styles.container} - sx={style} + style={{ ...style, ...paginatedStyles }} header={ void; total?: number; + style?: React.CSSProperties; } const staticColumns = ['Select', 'Actions', 'name', 'favorite']; @@ -91,6 +92,7 @@ export const ProjectFeatureToggles = ({ environments: newEnvironments = [], onChange, total, + style = {}, }: IProjectFeatureTogglesProps) => { const { classes: styles } = useStyles(); const theme = useTheme(); @@ -506,6 +508,7 @@ export const ProjectFeatureToggles = ({ isLoading={loading} disablePadding className={styles.container} + style={style} header={ ({