diff --git a/frontend/src/assets/img/upgradeProjects.png b/frontend/src/assets/img/upgradeProjects.png new file mode 100644 index 000000000000..183e56d0293d Binary files /dev/null and b/frontend/src/assets/img/upgradeProjects.png differ diff --git a/frontend/src/component/project/ProjectCard/ProjectCard.styles.ts b/frontend/src/component/project/ProjectCard/ProjectCard.styles.ts index f83c93139e04..5025a55f7912 100644 --- a/frontend/src/component/project/ProjectCard/ProjectCard.styles.ts +++ b/frontend/src/component/project/ProjectCard/ProjectCard.styles.ts @@ -32,6 +32,7 @@ export const StyledProjectCardBody = styled(Box)(({ theme }) => ({ flexFlow: 'column', justifyContent: 'space-between', height: '100%', + position: 'relative', })); export const StyledDivHeader = styled('div')(({ theme }) => ({ diff --git a/frontend/src/component/project/ProjectCard/ProjectCardFooter/ProjectCardFooter.tsx b/frontend/src/component/project/ProjectCard/ProjectCardFooter/ProjectCardFooter.tsx index 3ed959960000..13d8c38a6dc9 100644 --- a/frontend/src/component/project/ProjectCard/ProjectCardFooter/ProjectCardFooter.tsx +++ b/frontend/src/component/project/ProjectCard/ProjectCardFooter/ProjectCardFooter.tsx @@ -11,7 +11,7 @@ interface IProjectCardFooterProps { isFavorite?: boolean; children?: React.ReactNode; disabled?: boolean; - owners: IProjectOwnersProps['owners']; + owners?: IProjectOwnersProps['owners']; } const StyledFooter = styled(Box)<{ disabled: boolean }>( @@ -34,7 +34,7 @@ export const ProjectCardFooter: FC = ({ }) => { return ( - + {owners ? : null} {children} ); diff --git a/frontend/src/component/project/ProjectCard/UpgradeProjectCard.tsx b/frontend/src/component/project/ProjectCard/UpgradeProjectCard.tsx new file mode 100644 index 000000000000..4e06da3ba891 --- /dev/null +++ b/frontend/src/component/project/ProjectCard/UpgradeProjectCard.tsx @@ -0,0 +1,110 @@ +import { IconButton, styled, Tooltip, Typography } from '@mui/material'; +import ArrowForwardIcon from '@mui/icons-material/ArrowForward'; +import CloseIcon from '@mui/icons-material/Close'; +import { StyledProjectCard, StyledProjectCardBody } from './ProjectCard.styles'; +import { ProjectCardFooter } from './ProjectCardFooter/ProjectCardFooter'; +import upgradeProjects from 'assets/img/upgradeProjects.png'; +import { formatAssetPath } from 'utils/formatPath'; +import { useLocalStorageState } from 'hooks/useLocalStorageState'; + +const StyledFooter = styled('div')(({ theme }) => ({ + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + padding: theme.spacing(0.5, 1, 0.5, 2), + height: 53, +})); + +const StyledCloseButton = styled(IconButton)(({ theme }) => ({ + position: 'absolute', + top: theme.spacing(0.75), + right: theme.spacing(0.75), +})); + +const StyledInfo = styled('a')(({ theme }) => ({ + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + gap: theme.spacing(1), + textDecoration: 'none', + color: 'inherit', + height: '100%', + paddingTop: theme.spacing(0.5), +})); + +const StyledImage = styled('img')(({ theme }) => ({ + width: 95, + margin: theme.spacing(1), +})); + +export const UpgradeProjectCard = () => { + const [moreProjectsUpgrade, setMoreProjectsUpgrade] = useLocalStorageState< + 'open' | 'closed' + >('upgrade-projects:v1', 'open'); + + if (moreProjectsUpgrade === 'closed') { + return null; + } + + const onDismiss = () => { + setMoreProjectsUpgrade('closed'); + }; + + return ( + + + + + + + + + + More{' '} + + projects + {' '} + – +
+ easy collaboration +
+ +
+
+ + + + Get unlimited projects, and scale Unleash in your + organization + + + + + + +
+ ); +}; diff --git a/frontend/src/component/project/ProjectList/ProjectGroup.tsx b/frontend/src/component/project/ProjectList/ProjectGroup.tsx index f07f0ddb850e..66c12c6da990 100644 --- a/frontend/src/component/project/ProjectList/ProjectGroup.tsx +++ b/frontend/src/component/project/ProjectList/ProjectGroup.tsx @@ -1,32 +1,14 @@ import type { ComponentType, ReactNode } from 'react'; import { Link } from 'react-router-dom'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; -import { ProjectCard as NewProjectCard } from '../ProjectCard/ProjectCard'; +import { ProjectCard as DefaultProjectCard } 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 { styled } from '@mui/material'; import { useSearchHighlightContext } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext'; -import { flexColumn } from 'themes/themeStyles'; - -const StyledContainer = styled('article')(({ theme }) => ({ - ...flexColumn, - gap: theme.spacing(2), -})); - -const StyledHeaderContainer = styled('div')(({ theme }) => ({ - display: 'flex', - flexDirection: 'column-reverse', - gap: theme.spacing(2), - [theme.breakpoints.up('md')]: { - flexDirection: 'row', - alignItems: 'flex-end', - }, -})); - -const StyledHeaderTitle = styled('div')(() => ({ - flexGrow: 0, -})); +import { UpgradeProjectCard } from '../ProjectCard/UpgradeProjectCard'; +import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; const StyledGridContainer = styled('div')(({ theme }) => ({ display: 'grid', @@ -56,41 +38,18 @@ type ProjectGroupProps = { }; export const ProjectGroup = ({ - sectionTitle, - sectionSubtitle, - HeaderActions, projects, loading, placeholder = 'No projects available.', ProjectCardComponent, link = true, }: ProjectGroupProps) => { - const ProjectCard = ProjectCardComponent ?? NewProjectCard; + const ProjectCard = ProjectCardComponent ?? DefaultProjectCard; + const { isOss } = useUiConfig(); const { searchQuery } = useSearchHighlightContext(); return ( - - - - - {sectionTitle} - - } - /> - - {sectionSubtitle} - - } - /> - - {HeaderActions} - + <> )} /> + {isOss() ? : null} } /> - + ); }; diff --git a/frontend/src/component/project/ProjectList/ProjectList.tsx b/frontend/src/component/project/ProjectList/ProjectList.tsx index b1bd18d4d895..56ec5ca024c3 100644 --- a/frontend/src/component/project/ProjectList/ProjectList.tsx +++ b/frontend/src/component/project/ProjectList/ProjectList.tsx @@ -16,6 +16,8 @@ import { ProjectCreationButton } from './ProjectCreationButton/ProjectCreationBu import { useGroupedProjects } from './hooks/useGroupedProjects'; import { useProjectsSearchAndSort } from './hooks/useProjectsSearchAndSort'; import { ProjectArchiveLink } from './ProjectArchiveLink/ProjectArchiveLink'; +import { ProjectsListHeader } from './ProjectsListHeader/ProjectsListHeader'; +import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; const StyledApiError = styled(ApiError)(({ theme }) => ({ maxWidth: '500px', @@ -30,6 +32,7 @@ const StyledContainer = styled('div')(({ theme }) => ({ export const ProjectList = () => { const { projects, loading, error, refetch } = useProjects(); + const { isOss } = useUiConfig(); const isSmallScreen = useMediaQuery(theme.breakpoints.down('md')); @@ -63,7 +66,7 @@ export const ProjectList = () => { actions={ <> { } > { )} /> - - setState({ - sortBy: sortBy as typeof state.sortBy, - }) - } +
+ + setState({ + sortBy: sortBy as typeof state.sortBy, + }) + } + /> + } + > + My projects + + +
+ {!isOss() ? ( +
+ + Other projects + + - } - loading={loading} - projects={groupedProjects.myProjects} - /> - - +
+ ) : null}
diff --git a/frontend/src/component/project/ProjectList/ProjectsListHeader/ProjectsListHeader.tsx b/frontend/src/component/project/ProjectList/ProjectsListHeader/ProjectsListHeader.tsx new file mode 100644 index 000000000000..fdf630f479c5 --- /dev/null +++ b/frontend/src/component/project/ProjectList/ProjectsListHeader/ProjectsListHeader.tsx @@ -0,0 +1,47 @@ +import { styled, Typography } from '@mui/material'; +import type { FC, ReactNode } from 'react'; + +type ProjectsListHeaderProps = { + children?: ReactNode; + subtitle?: string; + actions?: ReactNode; +}; + +const StyledHeaderContainer = styled('div')(({ theme }) => ({ + display: 'flex', + flexDirection: 'column-reverse', + gap: theme.spacing(2), + [theme.breakpoints.up('md')]: { + flexDirection: 'row', + alignItems: 'flex-end', + }, + marginBottom: theme.spacing(2), +})); + +const StyledHeaderTitle = styled('div')(() => ({ + flexGrow: 0, +})); + +export const ProjectsListHeader: FC = ({ + children, + subtitle, + actions, +}) => { + return ( + + + {children ? ( + + {children} + + ) : null} + {subtitle ? ( + + {subtitle} + + ) : null} + + {actions} + + ); +};