diff --git a/frontend/src/component/project/ProjectCard/LegacyProjectOwners/LegacyProjectOwners.tsx b/frontend/src/component/project/ProjectCard/LegacyProjectOwners/LegacyProjectOwners.tsx deleted file mode 100644 index 3f9d22864b..0000000000 --- a/frontend/src/component/project/ProjectCard/LegacyProjectOwners/LegacyProjectOwners.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import type { FC } from 'react'; -import { styled } from '@mui/material'; -import type { ProjectSchema, ProjectSchemaOwners } from 'openapi'; -import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; -import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; -import { AvatarGroup } from 'component/common/AvatarGroup/AvatarGroup'; - -export interface IProjectOwnersProps { - owners?: ProjectSchema['owners']; -} - -const useOwnersMap = () => { - const { uiConfig } = useUiConfig(); - - return ( - owner: ProjectSchemaOwners[0], - ): { - name: string; - imageUrl?: string; - email?: string; - } => { - if (owner.ownerType === 'user') { - return { - name: owner.name, - imageUrl: owner.imageUrl || undefined, - email: owner.email || undefined, - }; - } - if (owner.ownerType === 'group') { - return { - name: owner.name, - }; - } - return { - name: 'System', - imageUrl: `${uiConfig.unleashUrl}/logo-unleash.png`, - }; - }; -}; - -const StyledUserName = styled('p')(({ theme }) => ({ - fontSize: theme.typography.body1.fontSize, - margin: theme.spacing(0, 0, 0.5, 0), - overflowX: 'hidden', - textOverflow: 'ellipsis', - textWrap: 'nowrap', - alignSelf: 'end', -})); - -const StyledContainer = styled('div')(() => ({ - display: 'flex', - flexDirection: 'column', -})); - -const StyledHeader = styled('h3')(({ theme }) => ({ - margin: theme.spacing(0, 0, 1), - fontSize: theme.typography.caption.fontSize, - color: theme.palette.text.primary, - fontWeight: theme.typography.fontWeightRegular, -})); - -const StyledWrapper = styled('div')(({ theme }) => ({ - padding: theme.spacing(1.5, 0, 2.5, 3), - display: 'flex', - alignItems: 'center', - minWidth: 0, -})); - -export const ProjectOwners: FC = ({ owners = [] }) => { - const ownersMap = useOwnersMap(); - const users = owners.map(ownersMap); - - return ( - - - - {owners.length === 1 ? 'Owner' : 'Owners'} - - - - {users[0]?.name}} - elseShow={
} - /> - - ); -}; diff --git a/frontend/src/component/project/ProjectCard/ProjectCardFooter/ProjectCardFooter.tsx b/frontend/src/component/project/ProjectCard/ProjectCardFooter/ProjectCardFooter.tsx index 8c286ddb1e..3ed9599600 100644 --- a/frontend/src/component/project/ProjectCard/ProjectCardFooter/ProjectCardFooter.tsx +++ b/frontend/src/component/project/ProjectCard/ProjectCardFooter/ProjectCardFooter.tsx @@ -2,12 +2,9 @@ import type React from 'react'; import type { FC } from 'react'; import { Box, styled } from '@mui/material'; import { + ProjectOwners, type IProjectOwnersProps, - ProjectOwners as LegacyProjectOwners, -} from '../LegacyProjectOwners/LegacyProjectOwners'; -import { ProjectOwners } from './ProjectOwners/ProjectOwners'; -import { useUiFlag } from 'hooks/useUiFlag'; -import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; +} from './ProjectOwners/ProjectOwners'; interface IProjectCardFooterProps { id?: string; @@ -31,20 +28,13 @@ const StyledFooter = styled(Box)<{ disabled: boolean }>( ); export const ProjectCardFooter: FC = ({ - id, children, owners, disabled = false, }) => { - const projectListImprovementsEnabled = useUiFlag('projectListImprovements'); - return ( - } - elseShow={} - /> + {children} ); diff --git a/frontend/src/component/project/ProjectCard/ProjectModeBadge/ProjectModeBadge.tsx b/frontend/src/component/project/ProjectCard/ProjectModeBadge/ProjectModeBadge.tsx index d2796b2657..92bc99ef36 100644 --- a/frontend/src/component/project/ProjectCard/ProjectModeBadge/ProjectModeBadge.tsx +++ b/frontend/src/component/project/ProjectCard/ProjectModeBadge/ProjectModeBadge.tsx @@ -1,11 +1,7 @@ import type { FC } from 'react'; -import LockIcon from '@mui/icons-material/Lock'; import ProtectedProjectIcon from '@mui/icons-material/LockOutlined'; -import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; import PrivateProjectIcon from '@mui/icons-material/VisibilityOffOutlined'; import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip'; -import { Badge } from 'component/common/Badge/Badge'; -import { useUiFlag } from 'hooks/useUiFlag'; import { styled } from '@mui/material'; interface IProjectModeBadgeProps { @@ -19,50 +15,28 @@ const StyledIcon = styled('div')(({ theme }) => ({ })); export const ProjectModeBadge: FC = ({ mode }) => { - const projectListImprovementsEnabled = useUiFlag('projectListImprovements'); - if (mode === 'private') { - if (projectListImprovementsEnabled) { - return ( - - - - - - ); - } return ( - } /> + + + ); } if (mode === 'protected') { - if (projectListImprovementsEnabled) { - return ( - - - - - - ); - } return ( - } /> + + + ); } diff --git a/frontend/src/component/project/ProjectList/ArchiveProjectList.tsx b/frontend/src/component/project/ProjectList/ArchiveProjectList.tsx index 84c3897f90..21bfa988fd 100644 --- a/frontend/src/component/project/ProjectList/ArchiveProjectList.tsx +++ b/frontend/src/component/project/ProjectList/ArchiveProjectList.tsx @@ -140,7 +140,6 @@ export const ArchiveProjectList: FC = () => { ({ - maxWidth: '500px', - marginBottom: theme.spacing(2), -})); - -const StyledContainer = styled('div')(({ theme }) => ({ - display: 'flex', - flexDirection: 'column', - gap: theme.spacing(4), -})); - -type PageQueryType = Partial>; - -interface ICreateButtonData { - disabled: boolean; - tooltip?: Omit; - endIcon?: React.ReactNode; -} - -const NAVIGATE_TO_CREATE_PROJECT = 'NAVIGATE_TO_CREATE_PROJECT'; - -function resolveCreateButtonData( - isOss: boolean, - hasAccess: boolean, -): ICreateButtonData { - if (isOss) { - return { - disabled: true, - tooltip: { - titleComponent: ( - - ), - sx: { maxWidth: '320px' }, - variant: 'custom', - }, - endIcon: ( - } - lightmode={} - /> - ), - }; - } else if (!hasAccess) { - return { - tooltip: { - title: 'You do not have permission to create new projects', - }, - disabled: true, - }; - } else { - return { - tooltip: { title: 'Click to create a new project' }, - disabled: false, - }; - } -} - -const ProjectCreationButton: FC = () => { - const [searchParams] = useSearchParams(); - const showCreateDialog = Boolean(searchParams.get('create')); - const [openCreateDialog, setOpenCreateDialog] = useState(showCreateDialog); - const { hasAccess } = useContext(AccessContext); - const { isOss, loading } = useUiConfig(); - - const createButtonData = resolveCreateButtonData( - isOss(), - hasAccess(CREATE_PROJECT), - ); - - return ( - <> - setOpenCreateDialog(true)} - maxWidth='700px' - permission={CREATE_PROJECT} - disabled={createButtonData.disabled || loading} - tooltipProps={createButtonData.tooltip} - data-testid={NAVIGATE_TO_CREATE_PROJECT} - > - New project - - setOpenCreateDialog(false)} - /> - - ); -}; - -export const ProjectList = () => { - const { projects, loading, error, refetch } = useProjects(); - - const isSmallScreen = useMediaQuery(theme.breakpoints.down('md')); - const [searchParams, setSearchParams] = useSearchParams(); - const [searchValue, setSearchValue] = useState( - searchParams.get('search') || '', - ); - - const myProjects = new Set(useProfile().profile?.projects || []); - - useEffect(() => { - const tableState: PageQueryType = {}; - if (searchValue) { - tableState.search = searchValue; - } - - setSearchParams(tableState, { - replace: true, - }); - }, [searchValue, setSearchParams]); - - const filteredProjects = useMemo(() => { - const regExp = safeRegExp(searchValue, 'i'); - return ( - searchValue - ? projects.filter((project) => regExp.test(project.name)) - : projects - ).sort((a, b) => { - if (a?.favorite && !b?.favorite) { - return -1; - } - if (!a?.favorite && b?.favorite) { - return 1; - } - return 0; - }); - }, [projects, searchValue]); - - const groupedProjects = useMemo(() => { - return groupProjects(myProjects, filteredProjects); - }, [filteredProjects, myProjects]); - - const projectCount = - filteredProjects.length < projects.length - ? `${filteredProjects.length} of ${projects.length}` - : projects.length; - - const ProjectGroupComponent = (props: { - sectionTitle?: string; - projects: ProjectSchema[]; - }) => { - return ( - - ); - }; - - return ( - - - - - - } - /> - <> - - Archived projects - - - - - - - } - > - - } - /> - - } - > - - ( - - )} - /> - - - - - - ); -}; diff --git a/frontend/src/component/project/ProjectList/ProjectGroup.tsx b/frontend/src/component/project/ProjectList/ProjectGroup.tsx index 001666e51f..f07f0ddb85 100644 --- a/frontend/src/component/project/ProjectList/ProjectGroup.tsx +++ b/frontend/src/component/project/ProjectList/ProjectGroup.tsx @@ -1,13 +1,11 @@ import type { ComponentType, ReactNode } from 'react'; import { Link } from 'react-router-dom'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; -import { ProjectCard as LegacyProjectCard } from '../ProjectCard/LegacyProjectCard'; import { ProjectCard as NewProjectCard } from '../ProjectCard/ProjectCard'; import type { ProjectSchema } from 'openapi'; import loadingData from './loadingData'; import { TablePlaceholder } from 'component/common/Table'; import { styled, Typography } from '@mui/material'; -import { useUiFlag } from 'hooks/useUiFlag'; import { useSearchHighlightContext } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext'; import { flexColumn } from 'themes/themeStyles'; @@ -52,10 +50,6 @@ type ProjectGroupProps = { HeaderActions?: ReactNode; projects: ProjectSchema[]; loading: boolean; - /** - * @deprecated remove with projectListImprovements - */ - searchValue?: string; placeholder?: string; ProjectCardComponent?: ComponentType; link?: boolean; @@ -67,15 +61,11 @@ export const ProjectGroup = ({ HeaderActions, projects, loading, - searchValue, placeholder = 'No projects available.', ProjectCardComponent, link = true, }: ProjectGroupProps) => { - const projectListImprovementsEnabled = useUiFlag('projectListImprovements'); - const ProjectCard = - ProjectCardComponent ?? - (projectListImprovementsEnabled ? NewProjectCard : LegacyProjectCard); + const ProjectCard = ProjectCardComponent ?? NewProjectCard; const { searchQuery } = useSearchHighlightContext(); return ( @@ -91,10 +81,7 @@ export const ProjectGroup = ({ } /> {sectionSubtitle} @@ -108,11 +95,11 @@ export const ProjectGroup = ({ condition={projects.length < 1 && !loading} show={ 0} + condition={searchQuery?.length > 0} show={ No projects found matching “ - {searchValue || searchQuery} + {searchQuery} ” } diff --git a/frontend/src/component/project/ProjectList/ProjectList.tsx b/frontend/src/component/project/ProjectList/ProjectList.tsx index 801f59039f..cd6ecadd93 100644 --- a/frontend/src/component/project/ProjectList/ProjectList.tsx +++ b/frontend/src/component/project/ProjectList/ProjectList.tsx @@ -1,4 +1,4 @@ -import { type FC, useCallback } from 'react'; +import { useCallback } from 'react'; import useProjects from 'hooks/api/getters/useProjects/useProjects'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { PageContent } from 'component/common/PageContent/PageContent'; @@ -9,11 +9,9 @@ import theme from 'themes/theme'; import { Search } from 'component/common/Search/Search'; import { useProfile } from 'hooks/api/getters/useProfile/useProfile'; import { ProjectGroup } from './ProjectGroup'; -import { useUiFlag } from 'hooks/useUiFlag'; import { ProjectsListSort } from './ProjectsListSort/ProjectsListSort'; import { useProjectsListState } from './hooks/useProjectsListState'; import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext'; -import { ProjectList as LegacyProjectList } from './LegacyProjectList'; import { ProjectCreationButton } from './ProjectCreationButton/ProjectCreationButton'; import { useGroupedProjects } from './hooks/useGroupedProjects'; import { useProjectsSearchAndSort } from './hooks/useProjectsSearchAndSort'; @@ -30,7 +28,7 @@ const StyledContainer = styled('div')(({ theme }) => ({ gap: theme.spacing(6), })); -const NewProjectList = () => { +export const ProjectList = () => { const { projects, loading, error, refetch } = useProjects(); const isSmallScreen = useMediaQuery(theme.breakpoints.down('md')); @@ -140,13 +138,3 @@ const NewProjectList = () => { ); }; - -export const ProjectList: FC = () => { - const projectListImprovementsEnabled = useUiFlag('projectListImprovements'); - - if (projectListImprovementsEnabled) { - return ; - } - - return ; -}; diff --git a/frontend/src/interfaces/uiConfig.ts b/frontend/src/interfaces/uiConfig.ts index 784192cf63..837999b05e 100644 --- a/frontend/src/interfaces/uiConfig.ts +++ b/frontend/src/interfaces/uiConfig.ts @@ -86,7 +86,6 @@ export type UiFlags = { enableLegacyVariants?: boolean; navigationSidebar?: boolean; flagCreator?: boolean; - projectListImprovements?: boolean; onboardingUI?: boolean; eventTimeline?: boolean; personalDashboardUI?: boolean; diff --git a/src/lib/types/experimental.ts b/src/lib/types/experimental.ts index fb75b941a6..764e8fd23a 100644 --- a/src/lib/types/experimental.ts +++ b/src/lib/types/experimental.ts @@ -56,7 +56,6 @@ export type IFlagKey = | 'extendedMetrics' | 'removeUnsafeInlineStyleSrc' | 'originMiddleware' - | 'projectListImprovements' | 'useProjectReadModel' | 'addonUsageMetrics' | 'onboardingMetrics' @@ -280,10 +279,6 @@ const flags: IFlags = { process.env.UNLEASH_EXPERIMENTAL_ORIGIN_MIDDLEWARE, false, ), - projectListImprovements: parseEnvVarBoolean( - process.env.UNLEASH_EXPERIMENTAL_PROJECT_LIST_IMPROVEMENTS, - false, - ), useProjectReadModel: parseEnvVarBoolean( process.env.UNLEASH_EXPERIMENTAL_USE_PROJECT_READ_MODEL, false, diff --git a/src/server-dev.ts b/src/server-dev.ts index 5cdb8a7d58..934e616af7 100644 --- a/src/server-dev.ts +++ b/src/server-dev.ts @@ -51,7 +51,6 @@ process.nextTick(async () => { enableLegacyVariants: false, extendedMetrics: true, originMiddleware: true, - projectListImprovements: true, useProjectReadModel: true, addonUsageMetrics: true, onboardingMetrics: true,