You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@devlake.apache.org by li...@apache.org on 2023/05/30 02:00:59 UTC
[incubator-devlake] branch main updated: feat(config-ui): support delete blueprint and project (#5305)
This is an automated email from the ASF dual-hosted git repository.
likyh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git
The following commit(s) were added to refs/heads/main by this push:
new f03cec04f feat(config-ui): support delete blueprint and project (#5305)
f03cec04f is described below
commit f03cec04f2023e681739c102f37f945cc4690515
Author: 青湛 <0x...@gmail.com>
AuthorDate: Tue May 30 10:00:54 2023 +0800
feat(config-ui): support delete blueprint and project (#5305)
* feat(config-ui): support params align in buttons component
* feat(config-ui): support delete project
* feat(config-ui): support delete blueprint
---
config-ui/src/components/buttons/index.tsx | 22 +++++--
.../pages/blueprint/detail/blueprint-detail.tsx | 46 ++++++++++++-
config-ui/src/pages/blueprint/detail/styled.ts | 10 +++
config-ui/src/pages/project/detail/api.ts | 5 ++
.../src/pages/project/detail/settings-panel.tsx | 75 +++++++++++++++++-----
config-ui/src/pages/project/detail/styled.ts | 10 +++
6 files changed, 146 insertions(+), 22 deletions(-)
diff --git a/config-ui/src/components/buttons/index.tsx b/config-ui/src/components/buttons/index.tsx
index 8734aa563..ac5dd647e 100644
--- a/config-ui/src/components/buttons/index.tsx
+++ b/config-ui/src/components/buttons/index.tsx
@@ -18,21 +18,35 @@
import styled from 'styled-components';
-const Wrapper = styled.div`
+const Wrapper = styled.div<{ align: 'left' | 'right' | 'center' }>`
display: flex;
align-items: center;
- justify-content: end;
margin-top: 24px;
+ ${({ align }) => {
+ if (align === 'left') {
+ return 'justify-content: flex-start;';
+ }
+
+ if (align === 'right') {
+ return 'justify-content: flex-end;';
+ }
+
+ if (align === 'center') {
+ return 'justify-content: space-around;';
+ }
+ }}
+
.bp4-button + .bp4-button {
margin-left: 8px;
}
`;
interface Props {
+ align?: 'left' | 'right' | 'center';
children: React.ReactNode;
}
-export const Buttons = ({ children }: Props) => {
- return <Wrapper>{children}</Wrapper>;
+export const Buttons = ({ align = 'right', children }: Props) => {
+ return <Wrapper align={align}>{children}</Wrapper>;
};
diff --git a/config-ui/src/pages/blueprint/detail/blueprint-detail.tsx b/config-ui/src/pages/blueprint/detail/blueprint-detail.tsx
index dc8faff31..36cf81e99 100644
--- a/config-ui/src/pages/blueprint/detail/blueprint-detail.tsx
+++ b/config-ui/src/pages/blueprint/detail/blueprint-detail.tsx
@@ -17,10 +17,11 @@
*/
import { useState } from 'react';
+import { useHistory } from 'react-router-dom';
import type { TabId } from '@blueprintjs/core';
-import { Tabs, Tab, Switch } from '@blueprintjs/core';
+import { Tabs, Tab, Switch, Button, Icon, Intent } from '@blueprintjs/core';
-import { PageLoading } from '@/components';
+import { PageLoading, Dialog } from '@/components';
import { useRefreshData } from '@/hooks';
import { operator } from '@/utils';
@@ -37,6 +38,9 @@ export const BlueprintDetail = ({ id }: Props) => {
const [activeTab, setActiveTab] = useState<TabId>('configuration');
const [version, setVersion] = useState(1);
const [operating, setOperating] = useState(false);
+ const [isOpen, setIsOpen] = useState(false);
+
+ const history = useHistory();
const { ready, data } = useRefreshData(
async () => Promise.all([API.getBlueprint(id), API.getBlueprintPipelines(id)]),
@@ -77,6 +81,25 @@ export const BlueprintDetail = ({ id }: Props) => {
}
};
+ const handleShowDeleteDialog = () => {
+ setIsOpen(true);
+ };
+
+ const handleHideDeleteDialog = () => {
+ setIsOpen(false);
+ };
+
+ const handleDelete = async () => {
+ const [success] = await operator(() => API.deleteBluprint(id), {
+ setOperating,
+ formatMessage: () => 'Delete blueprint successful.',
+ });
+
+ if (success) {
+ history.push('/blueprints');
+ }
+ };
+
return (
<S.Wrapper>
<Tabs selectedTabId={activeTab} onChange={(at) => setActiveTab(at)}>
@@ -99,7 +122,26 @@ export const BlueprintDetail = ({ id }: Props) => {
checked={blueprint.enable}
onChange={(e) => handleUpdate({ enable: (e.target as HTMLInputElement).checked })}
/>
+ <Button intent={Intent.DANGER} text="Delete Blueprint" onClick={handleShowDeleteDialog} />
</Tabs>
+ <Dialog
+ isOpen={isOpen}
+ style={{ width: 820 }}
+ title="Are you sure you want to delete this Blueprint?"
+ okText="Confirm"
+ okLoading={operating}
+ onCancel={handleHideDeleteDialog}
+ onOk={handleDelete}
+ >
+ <S.DialogBody>
+ <Icon icon="warning-sign" />
+ <span>
+ Please note: deleting the Blueprint will not delete the historical data of the Data Scopes in this
+ Blueprint. If you would like to delete the historical data of Data Scopes, please visit the Connection page
+ and do so.
+ </span>
+ </S.DialogBody>
+ </Dialog>
</S.Wrapper>
);
};
diff --git a/config-ui/src/pages/blueprint/detail/styled.ts b/config-ui/src/pages/blueprint/detail/styled.ts
index 31af92f98..a7caedb95 100644
--- a/config-ui/src/pages/blueprint/detail/styled.ts
+++ b/config-ui/src/pages/blueprint/detail/styled.ts
@@ -22,6 +22,16 @@ export const Wrapper = styled.div`
padding-bottom: 24px;
`;
+export const DialogBody = styled.div`
+ display: flex;
+ align-items: center;
+
+ .bp4-icon {
+ margin-right: 8px;
+ color: #f4be55;
+ }
+`;
+
export const ConfigurationPanel = styled.div`
.block + .block {
margin-top: 36px;
diff --git a/config-ui/src/pages/project/detail/api.ts b/config-ui/src/pages/project/detail/api.ts
index 2c7c65581..a222dda7f 100644
--- a/config-ui/src/pages/project/detail/api.ts
+++ b/config-ui/src/pages/project/detail/api.ts
@@ -30,3 +30,8 @@ export const updateProject = (name: string, payload: Omit<ProjectType, 'blueprin
export const updateBlueprint = (id: ID, payload: BlueprintType) =>
request(`/blueprints/${id}`, { method: 'patch', data: payload });
+
+export const deleteProject = (name: string) =>
+ request(`/projects/${name}`, {
+ method: 'delete',
+ });
diff --git a/config-ui/src/pages/project/detail/settings-panel.tsx b/config-ui/src/pages/project/detail/settings-panel.tsx
index a271b05f7..701be104c 100644
--- a/config-ui/src/pages/project/detail/settings-panel.tsx
+++ b/config-ui/src/pages/project/detail/settings-panel.tsx
@@ -18,15 +18,16 @@
import { useEffect, useState } from 'react';
import { useHistory } from 'react-router-dom';
-import { InputGroup, Checkbox, Button, Intent } from '@blueprintjs/core';
+import { InputGroup, Checkbox, Button, Icon, Intent } from '@blueprintjs/core';
-import { Card, FormItem, Buttons, toast } from '@/components';
+import { Card, FormItem, Buttons, toast, Dialog } from '@/components';
import { operator } from '@/utils';
import type { ProjectType } from '../types';
import { validName } from '../utils';
import * as API from './api';
+import * as S from './styled';
interface Props {
project: ProjectType;
@@ -36,6 +37,7 @@ export const SettingsPanel = ({ project }: Props) => {
const [name, setName] = useState('');
const [enableDora, setEnableDora] = useState(false);
const [operating, setOperating] = useState(false);
+ const [isOpen, setIsOpen] = useState(false);
const history = useHistory();
@@ -75,21 +77,62 @@ export const SettingsPanel = ({ project }: Props) => {
}
};
+ const handleShowDeleteDialog = () => {
+ setIsOpen(true);
+ };
+
+ const handleHideDeleteDialog = () => {
+ setIsOpen(false);
+ };
+
+ const handleDelete = async () => {
+ const [success] = await operator(() => API.deleteProject(project.name), {
+ setOperating,
+ formatMessage: () => 'Delete project successful.',
+ });
+
+ if (success) {
+ history.push(`/projects`);
+ }
+ };
+
return (
- <Card>
- <FormItem label="Project Name" subLabel="Edit your project name with letters, numbers, -, _ or /" required>
- <InputGroup style={{ width: 386 }} value={name} onChange={(e) => setName(e.target.value)} />
- </FormItem>
- <FormItem subLabel="DORA metrics are four widely-adopted metrics for measuring software delivery performance.">
- <Checkbox
- label="Enable DORA Metrics"
- checked={enableDora}
- onChange={(e) => setEnableDora((e.target as HTMLInputElement).checked)}
- />
- </FormItem>
- <Buttons>
- <Button text="Save" loading={operating} disabled={!name} intent={Intent.PRIMARY} onClick={handleUpdate} />
+ <>
+ <Card>
+ <FormItem label="Project Name" subLabel="Edit your project name with letters, numbers, -, _ or /" required>
+ <InputGroup style={{ width: 386 }} value={name} onChange={(e) => setName(e.target.value)} />
+ </FormItem>
+ <FormItem subLabel="DORA metrics are four widely-adopted metrics for measuring software delivery performance.">
+ <Checkbox
+ label="Enable DORA Metrics"
+ checked={enableDora}
+ onChange={(e) => setEnableDora((e.target as HTMLInputElement).checked)}
+ />
+ </FormItem>
+ <Buttons align="left">
+ <Button text="Save" loading={operating} disabled={!name} intent={Intent.PRIMARY} onClick={handleUpdate} />
+ </Buttons>
+ </Card>
+ <Buttons align="center">
+ <Button intent={Intent.DANGER} text="Delete Project" onClick={handleShowDeleteDialog} />
</Buttons>
- </Card>
+ <Dialog
+ isOpen={isOpen}
+ style={{ width: 820 }}
+ title="Are you sure you want to delete this Project?"
+ okText="Confirm"
+ okLoading={operating}
+ onCancel={handleHideDeleteDialog}
+ onOk={handleDelete}
+ >
+ <S.DialogBody>
+ <Icon icon="warning-sign" />
+ <span>
+ This operation cannot be undone. Deleting a Data Connection will delete all data that have been collected in
+ this Connection.
+ </span>
+ </S.DialogBody>
+ </Dialog>
+ </>
);
};
diff --git a/config-ui/src/pages/project/detail/styled.ts b/config-ui/src/pages/project/detail/styled.ts
index 248651df2..cca7675d9 100644
--- a/config-ui/src/pages/project/detail/styled.ts
+++ b/config-ui/src/pages/project/detail/styled.ts
@@ -24,3 +24,13 @@ export const Wrapper = styled.div`
justify-content: center;
}
`;
+
+export const DialogBody = styled.div`
+ display: flex;
+ align-items: center;
+
+ .bp4-icon {
+ margin-right: 8px;
+ color: #f4be55;
+ }
+`;