1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-04-01 01:18:10 +02:00

New project page header (#1207)

* Added new layout

* Fixes
This commit is contained in:
sjaanus 2022-08-08 12:59:36 +03:00 committed by GitHub
parent c978ed6c6b
commit 7c00b760f8
5 changed files with 148 additions and 50 deletions

View File

@ -0,0 +1,50 @@
import { Dialogue } from 'component/common/Dialogue/Dialogue';
import React from 'react';
import { formatUnknownError } from 'utils/formatUnknownError';
import useProjectApi from 'hooks/api/actions/useProjectApi/useProjectApi';
import useProjects from 'hooks/api/getters/useProjects/useProjects';
import useToast from 'hooks/useToast';
interface IDeleteProjectDialogueProps {
project: string;
open: boolean;
onClose?: () => void;
onSuccess?: () => void;
}
export const DeleteProjectDialogue = ({
open,
onClose,
project,
onSuccess,
}: IDeleteProjectDialogueProps) => {
const { deleteProject } = useProjectApi();
const { refetch: refetchProjectOverview } = useProjects();
const { setToastData, setToastApiError } = useToast();
const onClick = async (e: React.SyntheticEvent) => {
e.preventDefault();
try {
await deleteProject(project);
refetchProjectOverview();
setToastData({
title: 'Deleted project',
type: 'success',
text: 'Successfully deleted project',
});
onSuccess?.();
} catch (ex: unknown) {
setToastApiError(formatUnknownError(ex));
}
onClose?.();
};
return (
<Dialogue
open={open}
onClick={onClick}
onClose={onClose}
title="Really delete project"
/>
);
};

View File

@ -18,7 +18,7 @@ export const useStyles = makeStyles()(theme => ({
marginBottom: '1rem',
},
innerContainer: {
padding: '1rem 2rem',
padding: '1.25rem 2rem',
display: 'flex',
alignItems: 'center',
},

View File

@ -4,21 +4,44 @@ import useLoading from 'hooks/useLoading';
import ApiError from 'component/common/ApiError/ApiError';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { useStyles } from './Project.styles';
import { Tab, Tabs } from '@mui/material';
import { Edit } from '@mui/icons-material';
import { styled, Tab, Tabs } from '@mui/material';
import { Delete, Edit } from '@mui/icons-material';
import useToast from 'hooks/useToast';
import useQueryParams from 'hooks/useQueryParams';
import { useEffect } from 'react';
import { useEffect, useState } from 'react';
import { ProjectAccess } from '../ProjectAccess/ProjectAccess';
import ProjectEnvironment from '../ProjectEnvironment/ProjectEnvironment';
import { ProjectFeaturesArchive } from './ProjectFeaturesArchive/ProjectFeaturesArchive';
import ProjectOverview from './ProjectOverview';
import ProjectHealth from './ProjectHealth/ProjectHealth';
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
import { UPDATE_PROJECT } from 'component/providers/AccessProvider/permissions';
import {
DELETE_PROJECT,
UPDATE_PROJECT,
} from 'component/providers/AccessProvider/permissions';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { Routes, Route, useLocation } from 'react-router-dom';
import { DeleteProjectDialogue } from './DeleteProject/DeleteProjectDialogue';
const StyledDiv = styled('div')(() => ({
display: 'flex',
}));
const StyledName = styled('div')(({ theme }) => ({
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
paddingBottom: theme.spacing(2),
}));
const StyledTitle = styled('span')(({ theme }) => ({
fontSize: theme.fontSizes.smallBody,
fontWeight: 'normal',
}));
const StyledText = styled(StyledTitle)(({ theme }) => ({
color: theme.palette.grey[800],
}));
const Project = () => {
const projectId = useRequiredPathParam('projectId');
@ -33,6 +56,8 @@ const Project = () => {
const basePath = `/projects/${projectId}`;
const projectName = project?.name || projectId;
const [showDelDialog, setShowDelDialog] = useState(false);
const tabs = [
{
title: 'Overview',
@ -85,21 +110,60 @@ const Project = () => {
<div className={styles.header}>
<div className={styles.innerContainer}>
<h2 className={styles.title}>
<div className={styles.titleText} data-loading>
{projectName}
<div>
<StyledName data-loading>{projectName}</StyledName>
<ConditionallyRender
condition={Boolean(project.description)}
show={
<StyledDiv>
<StyledTitle data-loading>
Description:&nbsp;
</StyledTitle>
<StyledText data-loading>
{project.description}
</StyledText>
</StyledDiv>
}
/>
<StyledDiv>
<StyledTitle data-loading>
projectId:&nbsp;
</StyledTitle>
<StyledText data-loading>
{projectId}
</StyledText>
</StyledDiv>
</div>
<PermissionIconButton
permission={UPDATE_PROJECT}
projectId={projectId}
sx={{ visibility: isOss() ? 'hidden' : 'visible' }}
onClick={() =>
navigate(`/projects/${projectId}/edit`)
}
tooltipProps={{ title: 'Edit project' }}
data-loading
>
<Edit />
</PermissionIconButton>
<StyledDiv>
<PermissionIconButton
permission={UPDATE_PROJECT}
projectId={projectId}
sx={{
visibility: isOss() ? 'hidden' : 'visible',
}}
onClick={() =>
navigate(`/projects/${projectId}/edit`)
}
tooltipProps={{ title: 'Edit project' }}
data-loading
>
<Edit />
</PermissionIconButton>
<PermissionIconButton
permission={DELETE_PROJECT}
projectId={projectId}
sx={{
visibility: isOss() ? 'hidden' : 'visible',
}}
onClick={() => {
setShowDelDialog(true);
}}
tooltipProps={{ title: 'Delete project' }}
data-loading
>
<Delete />
</PermissionIconButton>
</StyledDiv>
</h2>
</div>
<ConditionallyRender
@ -132,6 +196,16 @@ const Project = () => {
</Tabs>
</div>
</div>
<DeleteProjectDialogue
project={projectId}
open={showDelDialog}
onClose={() => {
setShowDelDialog(false);
}}
onSuccess={() => {
navigate('/projects');
}}
/>
<Routes>
<Route path="health" element={<ProjectHealth />} />
<Route path="access/*" element={<ProjectAccess />} />

View File

@ -32,6 +32,7 @@ export const useStyles = makeStyles()(theme => ({
boxOrient: 'vertical',
textOverflow: 'ellipsis',
overflow: 'hidden',
alignItems: 'flex-start',
},
projectIcon: {

View File

@ -4,21 +4,17 @@ import MoreVertIcon from '@mui/icons-material/MoreVert';
import { ReactComponent as ProjectIcon } from 'assets/icons/projectIcon.svg';
import React, { useState, SyntheticEvent, useContext } from 'react';
import { useNavigate } from 'react-router-dom';
import { Dialogue } from 'component/common/Dialogue/Dialogue';
import useProjectApi from 'hooks/api/actions/useProjectApi/useProjectApi';
import useProjects from 'hooks/api/getters/useProjects/useProjects';
import { Delete, Edit } from '@mui/icons-material';
import { getProjectEditPath } from 'utils/routePathHelpers';
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
import useToast from 'hooks/useToast';
import {
UPDATE_PROJECT,
DELETE_PROJECT,
} from 'component/providers/AccessProvider/permissions';
import { formatUnknownError } from 'utils/formatUnknownError';
import AccessContext from 'contexts/AccessContext';
import { DEFAULT_PROJECT_ID } from 'hooks/api/getters/useDefaultProject/useDefaultProjectId';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { DeleteProjectDialogue } from '../Project/DeleteProject/DeleteProjectDialogue';
interface IProjectCardProps {
name: string;
@ -40,36 +36,15 @@ export const ProjectCard = ({
const { classes } = useStyles();
const { hasAccess } = useContext(AccessContext);
const { isOss } = useUiConfig();
const { refetch: refetchProjectOverview } = useProjects();
const [anchorEl, setAnchorEl] = useState(null);
const [showDelDialog, setShowDelDialog] = useState(false);
const { deleteProject } = useProjectApi();
const navigate = useNavigate();
const { setToastData, setToastApiError } = useToast();
// @ts-expect-error
const handleClick = e => {
e.preventDefault();
setAnchorEl(e.currentTarget);
};
const onRemoveProject = async (e: React.SyntheticEvent) => {
e.preventDefault();
try {
await deleteProject(id);
refetchProjectOverview();
setToastData({
title: 'Deleted project',
type: 'success',
text: 'Successfully deleted project',
});
} catch (e: unknown) {
setToastApiError(formatUnknownError(e));
}
setShowDelDialog(false);
setAnchorEl(null);
};
const canDeleteProject =
hasAccess(DELETE_PROJECT, id) && id !== DEFAULT_PROJECT_ID;
@ -152,15 +127,13 @@ export const ProjectCard = ({
<p data-loading>members</p>
</div>
</div>
<Dialogue
<DeleteProjectDialogue
project={id}
open={showDelDialog}
onClick={onRemoveProject}
onClose={event => {
event.preventDefault();
onClose={() => {
setAnchorEl(null);
setShowDelDialog(false);
}}
title="Really delete project"
/>
</Card>
);