mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-17 13:46:47 +02:00
parent
c978ed6c6b
commit
7c00b760f8
@ -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"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
@ -18,7 +18,7 @@ export const useStyles = makeStyles()(theme => ({
|
|||||||
marginBottom: '1rem',
|
marginBottom: '1rem',
|
||||||
},
|
},
|
||||||
innerContainer: {
|
innerContainer: {
|
||||||
padding: '1rem 2rem',
|
padding: '1.25rem 2rem',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
|
@ -4,21 +4,44 @@ import useLoading from 'hooks/useLoading';
|
|||||||
import ApiError from 'component/common/ApiError/ApiError';
|
import ApiError from 'component/common/ApiError/ApiError';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { useStyles } from './Project.styles';
|
import { useStyles } from './Project.styles';
|
||||||
import { Tab, Tabs } from '@mui/material';
|
import { styled, Tab, Tabs } from '@mui/material';
|
||||||
import { Edit } from '@mui/icons-material';
|
import { Delete, Edit } from '@mui/icons-material';
|
||||||
import useToast from 'hooks/useToast';
|
import useToast from 'hooks/useToast';
|
||||||
import useQueryParams from 'hooks/useQueryParams';
|
import useQueryParams from 'hooks/useQueryParams';
|
||||||
import { useEffect } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { ProjectAccess } from '../ProjectAccess/ProjectAccess';
|
import { ProjectAccess } from '../ProjectAccess/ProjectAccess';
|
||||||
import ProjectEnvironment from '../ProjectEnvironment/ProjectEnvironment';
|
import ProjectEnvironment from '../ProjectEnvironment/ProjectEnvironment';
|
||||||
import { ProjectFeaturesArchive } from './ProjectFeaturesArchive/ProjectFeaturesArchive';
|
import { ProjectFeaturesArchive } from './ProjectFeaturesArchive/ProjectFeaturesArchive';
|
||||||
import ProjectOverview from './ProjectOverview';
|
import ProjectOverview from './ProjectOverview';
|
||||||
import ProjectHealth from './ProjectHealth/ProjectHealth';
|
import ProjectHealth from './ProjectHealth/ProjectHealth';
|
||||||
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
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 { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
import { Routes, Route, useLocation } from 'react-router-dom';
|
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 Project = () => {
|
||||||
const projectId = useRequiredPathParam('projectId');
|
const projectId = useRequiredPathParam('projectId');
|
||||||
@ -33,6 +56,8 @@ const Project = () => {
|
|||||||
const basePath = `/projects/${projectId}`;
|
const basePath = `/projects/${projectId}`;
|
||||||
const projectName = project?.name || projectId;
|
const projectName = project?.name || projectId;
|
||||||
|
|
||||||
|
const [showDelDialog, setShowDelDialog] = useState(false);
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{
|
{
|
||||||
title: 'Overview',
|
title: 'Overview',
|
||||||
@ -85,21 +110,60 @@ const Project = () => {
|
|||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
<div className={styles.innerContainer}>
|
<div className={styles.innerContainer}>
|
||||||
<h2 className={styles.title}>
|
<h2 className={styles.title}>
|
||||||
<div className={styles.titleText} data-loading>
|
<div>
|
||||||
{projectName}
|
<StyledName data-loading>{projectName}</StyledName>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={Boolean(project.description)}
|
||||||
|
show={
|
||||||
|
<StyledDiv>
|
||||||
|
<StyledTitle data-loading>
|
||||||
|
Description:
|
||||||
|
</StyledTitle>
|
||||||
|
<StyledText data-loading>
|
||||||
|
{project.description}
|
||||||
|
</StyledText>
|
||||||
|
</StyledDiv>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<StyledDiv>
|
||||||
|
<StyledTitle data-loading>
|
||||||
|
projectId:
|
||||||
|
</StyledTitle>
|
||||||
|
<StyledText data-loading>
|
||||||
|
{projectId}
|
||||||
|
</StyledText>
|
||||||
|
</StyledDiv>
|
||||||
</div>
|
</div>
|
||||||
<PermissionIconButton
|
<StyledDiv>
|
||||||
permission={UPDATE_PROJECT}
|
<PermissionIconButton
|
||||||
projectId={projectId}
|
permission={UPDATE_PROJECT}
|
||||||
sx={{ visibility: isOss() ? 'hidden' : 'visible' }}
|
projectId={projectId}
|
||||||
onClick={() =>
|
sx={{
|
||||||
navigate(`/projects/${projectId}/edit`)
|
visibility: isOss() ? 'hidden' : 'visible',
|
||||||
}
|
}}
|
||||||
tooltipProps={{ title: 'Edit project' }}
|
onClick={() =>
|
||||||
data-loading
|
navigate(`/projects/${projectId}/edit`)
|
||||||
>
|
}
|
||||||
<Edit />
|
tooltipProps={{ title: 'Edit project' }}
|
||||||
</PermissionIconButton>
|
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>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
@ -132,6 +196,16 @@ const Project = () => {
|
|||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<DeleteProjectDialogue
|
||||||
|
project={projectId}
|
||||||
|
open={showDelDialog}
|
||||||
|
onClose={() => {
|
||||||
|
setShowDelDialog(false);
|
||||||
|
}}
|
||||||
|
onSuccess={() => {
|
||||||
|
navigate('/projects');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="health" element={<ProjectHealth />} />
|
<Route path="health" element={<ProjectHealth />} />
|
||||||
<Route path="access/*" element={<ProjectAccess />} />
|
<Route path="access/*" element={<ProjectAccess />} />
|
||||||
|
@ -32,6 +32,7 @@ export const useStyles = makeStyles()(theme => ({
|
|||||||
boxOrient: 'vertical',
|
boxOrient: 'vertical',
|
||||||
textOverflow: 'ellipsis',
|
textOverflow: 'ellipsis',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
|
alignItems: 'flex-start',
|
||||||
},
|
},
|
||||||
|
|
||||||
projectIcon: {
|
projectIcon: {
|
||||||
|
@ -4,21 +4,17 @@ import MoreVertIcon from '@mui/icons-material/MoreVert';
|
|||||||
import { ReactComponent as ProjectIcon } from 'assets/icons/projectIcon.svg';
|
import { ReactComponent as ProjectIcon } from 'assets/icons/projectIcon.svg';
|
||||||
import React, { useState, SyntheticEvent, useContext } from 'react';
|
import React, { useState, SyntheticEvent, useContext } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
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 { Delete, Edit } from '@mui/icons-material';
|
||||||
import { getProjectEditPath } from 'utils/routePathHelpers';
|
import { getProjectEditPath } from 'utils/routePathHelpers';
|
||||||
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
||||||
import useToast from 'hooks/useToast';
|
|
||||||
import {
|
import {
|
||||||
UPDATE_PROJECT,
|
UPDATE_PROJECT,
|
||||||
DELETE_PROJECT,
|
DELETE_PROJECT,
|
||||||
} from 'component/providers/AccessProvider/permissions';
|
} from 'component/providers/AccessProvider/permissions';
|
||||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
|
||||||
import AccessContext from 'contexts/AccessContext';
|
import AccessContext from 'contexts/AccessContext';
|
||||||
import { DEFAULT_PROJECT_ID } from 'hooks/api/getters/useDefaultProject/useDefaultProjectId';
|
import { DEFAULT_PROJECT_ID } from 'hooks/api/getters/useDefaultProject/useDefaultProjectId';
|
||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
|
import { DeleteProjectDialogue } from '../Project/DeleteProject/DeleteProjectDialogue';
|
||||||
|
|
||||||
interface IProjectCardProps {
|
interface IProjectCardProps {
|
||||||
name: string;
|
name: string;
|
||||||
@ -40,36 +36,15 @@ export const ProjectCard = ({
|
|||||||
const { classes } = useStyles();
|
const { classes } = useStyles();
|
||||||
const { hasAccess } = useContext(AccessContext);
|
const { hasAccess } = useContext(AccessContext);
|
||||||
const { isOss } = useUiConfig();
|
const { isOss } = useUiConfig();
|
||||||
const { refetch: refetchProjectOverview } = useProjects();
|
|
||||||
const [anchorEl, setAnchorEl] = useState(null);
|
const [anchorEl, setAnchorEl] = useState(null);
|
||||||
const [showDelDialog, setShowDelDialog] = useState(false);
|
const [showDelDialog, setShowDelDialog] = useState(false);
|
||||||
const { deleteProject } = useProjectApi();
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { setToastData, setToastApiError } = useToast();
|
|
||||||
|
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
const handleClick = e => {
|
const handleClick = e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setAnchorEl(e.currentTarget);
|
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 =
|
const canDeleteProject =
|
||||||
hasAccess(DELETE_PROJECT, id) && id !== DEFAULT_PROJECT_ID;
|
hasAccess(DELETE_PROJECT, id) && id !== DEFAULT_PROJECT_ID;
|
||||||
|
|
||||||
@ -152,15 +127,13 @@ export const ProjectCard = ({
|
|||||||
<p data-loading>members</p>
|
<p data-loading>members</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Dialogue
|
<DeleteProjectDialogue
|
||||||
|
project={id}
|
||||||
open={showDelDialog}
|
open={showDelDialog}
|
||||||
onClick={onRemoveProject}
|
onClose={() => {
|
||||||
onClose={event => {
|
|
||||||
event.preventDefault();
|
|
||||||
setAnchorEl(null);
|
setAnchorEl(null);
|
||||||
setShowDelDialog(false);
|
setShowDelDialog(false);
|
||||||
}}
|
}}
|
||||||
title="Really delete project"
|
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user