From de74faac467c1e97d1217a89d9f5bd13986a7ccd Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Fri, 31 May 2024 10:58:31 +0200 Subject: [PATCH] chore: remove flag for new project cards (#7225) This PR removes the flag for the new project card design, making it GA. It also removes deprecated components and updates one reference (in the groups card) to the new components instead. --- .../groups/GroupsList/GroupCard/GroupCard.tsx | 2 +- .../GroupCardAvatars/GroupCardAvatars.tsx | 82 -------- .../GroupCardAvatars/NewGroupCardAvatars.tsx | 6 - .../project/ProjectCard/ProjectCard.styles.ts | 83 --------- .../project/ProjectCard/ProjectCard.tsx | 176 ------------------ .../project/ProjectList/ProjectGroup.tsx | 26 +-- frontend/src/interfaces/uiConfig.ts | 1 - .../__snapshots__/create-config.test.ts.snap | 1 - .../features/project/project-controller.ts | 25 +-- src/lib/types/experimental.ts | 5 - src/server-dev.ts | 1 - 11 files changed, 13 insertions(+), 395 deletions(-) delete mode 100644 frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCardAvatars/GroupCardAvatars.tsx delete mode 100644 frontend/src/component/project/ProjectCard/ProjectCard.styles.ts delete mode 100644 frontend/src/component/project/ProjectCard/ProjectCard.tsx diff --git a/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCard.tsx b/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCard.tsx index 4705eec80c..0f7034e40e 100644 --- a/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCard.tsx +++ b/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCard.tsx @@ -2,12 +2,12 @@ import { styled, Tooltip } from '@mui/material'; import type { IGroup } from 'interfaces/group'; import { Link, useNavigate } from 'react-router-dom'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; -import { GroupCardAvatars } from './GroupCardAvatars/GroupCardAvatars'; import { Badge } from 'component/common/Badge/Badge'; import { GroupCardActions } from './GroupCardActions/GroupCardActions'; import TopicOutlinedIcon from '@mui/icons-material/TopicOutlined'; import { RoleBadge } from 'component/common/RoleBadge/RoleBadge'; import { useScimSettings } from 'hooks/api/getters/useScimSettings/useScimSettings'; +import { GroupCardAvatars } from './GroupCardAvatars/NewGroupCardAvatars'; const StyledLink = styled(Link)(({ theme }) => ({ textDecoration: 'none', diff --git a/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCardAvatars/GroupCardAvatars.tsx b/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCardAvatars/GroupCardAvatars.tsx deleted file mode 100644 index 64313dc7c1..0000000000 --- a/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCardAvatars/GroupCardAvatars.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { styled } from '@mui/material'; -import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; -import type { IGroupUser } from 'interfaces/group'; -import type React from 'react'; -import { useMemo, useState } from 'react'; -import { GroupPopover } from './GroupPopover/GroupPopover'; -import { UserAvatar } from 'component/common/UserAvatar/UserAvatar'; - -const StyledAvatars = styled('div')(({ theme }) => ({ - display: 'inline-flex', - alignItems: 'center', - flexWrap: 'wrap', - marginLeft: theme.spacing(1), -})); - -const StyledAvatar = styled(UserAvatar)(({ theme }) => ({ - outline: `${theme.spacing(0.25)} solid ${theme.palette.background.paper}`, - marginLeft: theme.spacing(-1), - '&:hover': { - outlineColor: theme.palette.primary.main, - }, -})); - -interface IGroupCardAvatarsProps { - users: IGroupUser[]; -} - -/** - * @deprecated Remove after with `projectsListNewCards` flag - */ -export const GroupCardAvatars = ({ users }: IGroupCardAvatarsProps) => { - const shownUsers = useMemo( - () => - users - .sort((a, b) => b?.joinedAt!.getTime() - a?.joinedAt!.getTime()) - .slice(0, 9), - [users], - ); - - const [anchorEl, setAnchorEl] = useState(null); - const [popupUser, setPopupUser] = useState(); - - const onPopoverOpen = (event: React.MouseEvent) => { - setAnchorEl(event.currentTarget); - }; - - const onPopoverClose = () => { - setAnchorEl(null); - }; - - const avatarOpen = Boolean(anchorEl); - - return ( - - {shownUsers.map((user) => ( - { - onPopoverOpen(event); - setPopupUser(user); - }} - onMouseLeave={onPopoverClose} - /> - ))} - 9} - show={ - - +{users.length - shownUsers.length} - - } - /> - - - ); -}; diff --git a/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCardAvatars/NewGroupCardAvatars.tsx b/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCardAvatars/NewGroupCardAvatars.tsx index 17f9c503a9..adb8abfb4b 100644 --- a/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCardAvatars/NewGroupCardAvatars.tsx +++ b/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCardAvatars/NewGroupCardAvatars.tsx @@ -27,12 +27,6 @@ const StyledAvatar = styled(UserAvatar)(({ theme }) => ({ }, })); -const StyledUsername = styled('div')(({ theme }) => ({ - fontSize: theme.typography.body2.fontSize, - color: theme.palette.text.primary, - marginLeft: theme.spacing(1), -})); - const StyledHeader = styled('h3')(({ theme }) => ({ margin: theme.spacing(0, 0, 1), fontSize: theme.typography.caption.fontSize, diff --git a/frontend/src/component/project/ProjectCard/ProjectCard.styles.ts b/frontend/src/component/project/ProjectCard/ProjectCard.styles.ts deleted file mode 100644 index 5d3e73a6a3..0000000000 --- a/frontend/src/component/project/ProjectCard/ProjectCard.styles.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { styled } from '@mui/material'; -import { Card, Box } from '@mui/material'; -import Delete from '@mui/icons-material/Delete'; -import Edit from '@mui/icons-material/Edit'; -import { ReactComponent as ProjectIcon } from 'assets/icons/projectIcon.svg'; -import { flexRow } from 'themes/themeStyles'; - -export const StyledProjectCard = styled(Card)(({ theme }) => ({ - padding: theme.spacing(1, 2, 2, 2), - width: '220px', - height: '204px', - display: 'flex', - flexDirection: 'column', - justifyContent: 'space-between', - margin: theme.spacing(1), - boxShadow: 'none', - border: `1px solid ${theme.palette.divider}`, - [theme.breakpoints.down('sm')]: { - justifyContent: 'center', - }, - '&:hover': { - transition: 'background-color 0.2s ease-in-out', - backgroundColor: theme.palette.neutral.light, - }, -})); - -export const StyledDivHeader = styled('div')(() => ({ - ...flexRow, - width: '100%', -})); - -export const StyledH2Title = styled('h2')(({ theme }) => ({ - fontWeight: 'normal', - fontSize: theme.fontSizes.bodySize, - lineClamp: 2, - display: '-webkit-box', - boxOrient: 'vertical', - textOverflow: 'ellipsis', - overflow: 'hidden', - alignItems: 'flex-start', -})); - -export const StyledBox = styled(Box)(() => ({ - ...flexRow, - marginRight: 'auto', -})); - -export const StyledEditIcon = styled(Edit)(({ theme }) => ({ - color: theme.palette.neutral.main, - marginRight: theme.spacing(1), -})); - -export const StyledDeleteIcon = styled(Delete)(({ theme }) => ({ - color: theme.palette.neutral.main, - marginRight: theme.spacing(1), -})); - -export const StyledProjectIcon = styled(ProjectIcon)(({ theme }) => ({ - margin: theme.spacing(2, 'auto'), - width: '80px', - display: 'block', - fill: 'red', -})); - -export const StyledDivInfo = styled('div')(({ theme }) => ({ - display: 'flex', - justifyContent: 'space-between', - fontSize: theme.fontSizes.smallerBody, -})); - -export const StyledDivInfoContainer = styled('div')(() => ({ - textAlign: 'center', -})); - -export const StyledParagraphInfo = styled('p')(({ theme }) => ({ - color: theme.palette.primary.dark, - fontWeight: 'bold', -})); - -export const StyledIconBox = styled(Box)(() => ({ - display: 'flex', - justifyContent: 'center', -})); diff --git a/frontend/src/component/project/ProjectCard/ProjectCard.tsx b/frontend/src/component/project/ProjectCard/ProjectCard.tsx deleted file mode 100644 index 6ba1c57f65..0000000000 --- a/frontend/src/component/project/ProjectCard/ProjectCard.tsx +++ /dev/null @@ -1,176 +0,0 @@ -import { Menu, MenuItem } from '@mui/material'; -import MoreVertIcon from '@mui/icons-material/MoreVert'; -import type React from 'react'; -import { type SyntheticEvent, useContext, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; -import { getProjectEditPath } from 'utils/routePathHelpers'; -import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton'; -import { UPDATE_PROJECT } from 'component/providers/AccessProvider/permissions'; -import AccessContext from 'contexts/AccessContext'; -import { DEFAULT_PROJECT_ID } from 'hooks/api/getters/useDefaultProject/useDefaultProjectId'; -import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; -import useProjects from 'hooks/api/getters/useProjects/useProjects'; -import { useFavoriteProjectsApi } from 'hooks/api/actions/useFavoriteProjectsApi/useFavoriteProjectsApi'; -import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; -import { FavoriteIconButton } from 'component/common/FavoriteIconButton/FavoriteIconButton'; -import { DeleteProjectDialogue } from '../Project/DeleteProject/DeleteProjectDialogue'; -import { - StyledProjectCard, - StyledDivHeader, - StyledBox, - StyledH2Title, - StyledEditIcon, - StyledProjectIcon, - StyledDivInfo, - StyledDivInfoContainer, - StyledParagraphInfo, - StyledIconBox, -} from './ProjectCard.styles'; -import useToast from 'hooks/useToast'; -import { HiddenProjectIconWithTooltip } from '../Project/HiddenProjectIconWithTooltip/HiddenProjectIconWithTooltip'; - -interface IProjectCardProps { - name: string; - featureCount: number; - health: number; - memberCount: number; - id: string; - onHover: () => void; - isFavorite?: boolean; - mode: string; -} - -export const ProjectCard = ({ - name, - featureCount, - health, - memberCount, - onHover, - id, - mode, - isFavorite = false, -}: IProjectCardProps) => { - const { hasAccess } = useContext(AccessContext); - const { setToastApiError } = useToast(); - const { isOss } = useUiConfig(); - const [anchorEl, setAnchorEl] = useState(null); - const [showDelDialog, setShowDelDialog] = useState(false); - const navigate = useNavigate(); - const { favorite, unfavorite } = useFavoriteProjectsApi(); - const { refetch } = useProjects(); - - const handleClick = (event: React.SyntheticEvent) => { - event.preventDefault(); - setAnchorEl(event.currentTarget); - }; - - const onFavorite = async (e: React.SyntheticEvent) => { - e.preventDefault(); - try { - if (isFavorite) { - await unfavorite(id); - } else { - await favorite(id); - } - refetch(); - } catch (error) { - setToastApiError('Something went wrong, could not update favorite'); - } - }; - - return ( - - - - - {name} - - - - - { - event.preventDefault(); - }} - onClose={(event: SyntheticEvent) => { - event.preventDefault(); - setAnchorEl(null); - }} - > - { - e.preventDefault(); - navigate(getProjectEditPath(id)); - }} - > - - Edit project - - - - - } - elseShow={} - /> - - - - - {featureCount} - -

toggles

-
- - - {health}% - -

health

-
- - - - {memberCount} - -

members

- - } - /> -
- { - e.preventDefault(); - setAnchorEl(null); - setShowDelDialog(false); - }} - /> -
- ); -}; diff --git a/frontend/src/component/project/ProjectList/ProjectGroup.tsx b/frontend/src/component/project/ProjectList/ProjectGroup.tsx index 72a1945bc8..2c95b152e3 100644 --- a/frontend/src/component/project/ProjectList/ProjectGroup.tsx +++ b/frontend/src/component/project/ProjectList/ProjectGroup.tsx @@ -1,23 +1,11 @@ import { Link } from 'react-router-dom'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; -import { ProjectCard as LegacyProjectCard } from '../ProjectCard/ProjectCard'; -import { ProjectCard as NewProjectCard } from '../NewProjectCard/NewProjectCard'; +import { ProjectCard } from '../NewProjectCard/NewProjectCard'; + import type { IProjectCard } from 'interfaces/project'; import loadingData from './loadingData'; import { TablePlaceholder } from 'component/common/Table'; import { styled, Typography } from '@mui/material'; -import { useUiFlag } from 'hooks/useUiFlag'; - -/** - * @deprecated Remove after with `projectsListNewCards` flag - */ -const StyledDivContainer = styled('div')(({ theme }) => ({ - display: 'flex', - flexWrap: 'wrap', - [theme.breakpoints.down('sm')]: { - justifyContent: 'center', - }, -})); const StyledGridContainer = styled('div')(({ theme }) => ({ display: 'grid', @@ -41,12 +29,6 @@ export const ProjectGroup: React.FC<{ loading: boolean; searchValue: string; }> = ({ sectionTitle, projects, loading, searchValue }) => { - const useNewProjectCards = useUiFlag('projectsListNewCards'); - - const [StyledItemsContainer, ProjectCard] = useNewProjectCards - ? [StyledGridContainer, NewProjectCard] - : [StyledDivContainer, LegacyProjectCard]; - return (
} elseShow={ - + @@ -126,7 +108,7 @@ export const ProjectGroup: React.FC<{ )} /> - + } />
diff --git a/frontend/src/interfaces/uiConfig.ts b/frontend/src/interfaces/uiConfig.ts index 04767386f6..f6ecac82c3 100644 --- a/frontend/src/interfaces/uiConfig.ts +++ b/frontend/src/interfaces/uiConfig.ts @@ -82,7 +82,6 @@ export type UiFlags = { featureLifecycle?: boolean; scimApi?: boolean; createProjectWithEnvironmentConfig?: boolean; - projectsListNewCards?: boolean; newCreateProjectUI?: boolean; manyStrategiesPagination?: boolean; enableLegacyVariants?: boolean; diff --git a/src/lib/__snapshots__/create-config.test.ts.snap b/src/lib/__snapshots__/create-config.test.ts.snap index e28df3c6d5..1893ab606e 100644 --- a/src/lib/__snapshots__/create-config.test.ts.snap +++ b/src/lib/__snapshots__/create-config.test.ts.snap @@ -141,7 +141,6 @@ exports[`should create default config 1`] = ` "parseProjectFromSession": false, "personalAccessTokensKillSwitch": false, "projectOverviewRefactorFeedback": false, - "projectsListNewCards": false, "queryMissingTokens": false, "responseTimeMetricsFix": false, "responseTimeWithAppNameKillSwitch": false, diff --git a/src/lib/features/project/project-controller.ts b/src/lib/features/project/project-controller.ts index 0b7f12f4ba..8229c8fd30 100644 --- a/src/lib/features/project/project-controller.ts +++ b/src/lib/features/project/project-controller.ts @@ -223,24 +223,15 @@ export default class ProjectController extends Controller { user.id, ); - if (this.flagResolver.isEnabled('projectsListNewCards')) { - const projectsWithOwners = - await this.projectService.addOwnersToProjects(projects); + const projectsWithOwners = + await this.projectService.addOwnersToProjects(projects); - this.openApiService.respondWithValidation( - 200, - res, - projectsSchema.$id, - { version: 1, projects: serializeDates(projectsWithOwners) }, - ); - } else { - this.openApiService.respondWithValidation( - 200, - res, - projectsSchema.$id, - { version: 1, projects: serializeDates(projects) }, - ); - } + this.openApiService.respondWithValidation( + 200, + res, + projectsSchema.$id, + { version: 1, projects: serializeDates(projectsWithOwners) }, + ); } async getDeprecatedProjectOverview( diff --git a/src/lib/types/experimental.ts b/src/lib/types/experimental.ts index 9a97a62934..c71b8a3849 100644 --- a/src/lib/types/experimental.ts +++ b/src/lib/types/experimental.ts @@ -55,7 +55,6 @@ export type IFlagKey = | 'projectOverviewRefactorFeedback' | 'featureLifecycle' | 'featureLifecycleMetrics' - | 'projectsListNewCards' | 'parseProjectFromSession' | 'createProjectWithEnvironmentConfig' | 'manyStrategiesPagination' @@ -275,10 +274,6 @@ const flags: IFlags = { process.env.UNLEASH_EXPERIMENTAL_CREATE_PROJECT_WITH_ENVIRONMENT_CONFIG, false, ), - projectsListNewCards: parseEnvVarBoolean( - process.env.UNLEASH_EXPERIMENTAL_PROJECTS_LIST_NEW_CARDS, - false, - ), newCreateProjectUI: parseEnvVarBoolean( process.env.UNLEASH_EXPERIMENTAL_NEW_CREATE_PROJECT_UI, false, diff --git a/src/server-dev.ts b/src/server-dev.ts index 18656f02fd..b35f5f11de 100644 --- a/src/server-dev.ts +++ b/src/server-dev.ts @@ -49,7 +49,6 @@ process.nextTick(async () => { disableShowContextFieldSelectionValues: false, projectOverviewRefactorFeedback: true, featureLifecycle: true, - projectsListNewCards: true, parseProjectFromSession: true, createProjectWithEnvironmentConfig: true, manyStrategiesPagination: true,