This is an automated email from the ASF dual-hosted git repository.
jshao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/main by this push:
new 48368c9021 [#9975] web-v2(UI): Delete catalog optimization (#9994)
48368c9021 is described below
commit 48368c902174c38585f3a2494b2c7cc2a5e245a0
Author: Qian Xia <[email protected]>
AuthorDate: Fri Feb 27 16:25:44 2026 +0800
[#9975] web-v2(UI): Delete catalog optimization (#9994)
### What changes were proposed in this pull request?
Delete catalog optimization
delete catalog when it's `in use`
<img width="2604" height="1060" alt="image"
src="https://github.com/user-attachments/assets/e95b74d2-8dd2-4105-a03e-ff20fe199985"
/>
delete catalog when it's `not in use`
<img width="2830" height="1090" alt="image"
src="https://github.com/user-attachments/assets/723b8771-92b7-475f-ae2e-a54e4030d043"
/>
### Why are the changes needed?
When deleting a catalog, the copy or documentation is not clear enough
and the in-use sliding button cannot be found.
Fix: #9975
### Does this PR introduce _any_ user-facing change?
N/A
### How was this patch tested?
manually
---------
Co-authored-by: Copilot <[email protected]>
---
.../rightContent/entitiesContent/CatalogsPage.js | 86 ++++++++++++++--------
web-v2/web/src/lib/store/metalakes/index.js | 23 +++++-
2 files changed, 76 insertions(+), 33 deletions(-)
diff --git
a/web-v2/web/src/app/catalogs/rightContent/entitiesContent/CatalogsPage.js
b/web-v2/web/src/app/catalogs/rightContent/entitiesContent/CatalogsPage.js
index 33dff4aa2f..7c733648d6 100644
--- a/web-v2/web/src/app/catalogs/rightContent/entitiesContent/CatalogsPage.js
+++ b/web-v2/web/src/app/catalogs/rightContent/entitiesContent/CatalogsPage.js
@@ -19,15 +19,15 @@
'use client'
-import { createContext, useContext, useMemo, useState } from 'react'
+import { useContext, useMemo, useState } from 'react'
import dynamic from 'next/dynamic'
import { useSearchParams } from 'next/navigation'
import { ExclamationCircleFilled, PlusOutlined } from '@ant-design/icons'
-import { Button, Dropdown, Flex, Input, Modal, Spin, Table, Tooltip,
Typography } from 'antd'
+import { Button, Dropdown, Flex, Input, Modal, Spin, Switch, Table, Tooltip,
Typography } from 'antd'
import { useAntdColumnResize } from 'react-antd-column-resize'
import { useAppSelector, useAppDispatch } from '@/lib/hooks/useStore'
-import { switchInUseCatalog, deleteCatalog } from '@/lib/store/metalakes'
+import { switchInUseCatalog, deleteCatalog, updateCatalogInUse } from
'@/lib/store/metalakes'
import { TreeRefContext } from '../../page'
import Tags from '@/components/CustomTags'
import Icons from '@/components/Icons'
@@ -118,17 +118,9 @@ export default function CatalogsPage() {
setOpenOwner(true)
}
- const showDeleteConfirm = (NameContext, catalog, type) => {
- const inUse = catalog.properties['in-use'] === 'true'
- if (inUse) {
- modal.error({
- title: `Make sure the ${type} ${catalog.name} is not in-use`,
- icon: <ExclamationCircleFilled />,
- okText: 'OK'
- })
-
- return
- }
+ const showDeleteConfirm = (catalog, type) => {
+ const initialInUse = catalog.properties?.['in-use'] === 'true'
+ let currentInUse = initialInUse
let confirmInput = ''
let validateFn = null
@@ -140,25 +132,56 @@ export default function CatalogsPage() {
validateFn = fn
}
- modal.confirm({
- title: `Are you sure to delete the ${type} ${catalog.name}?`,
- icon: <ExclamationCircleFilled />,
- content: (
- <NameContext.Consumer>
- {name => (
+ const updateInUse = value => {
+ currentInUse = value
+ }
+
+ let confirmModal
+
+ const DeleteCatalogConfirmContent = () => {
+ const [inUse, setInUse] = useState(currentInUse)
+
+ const handleSwitchChange = async checked => {
+ setInUse(checked)
+ updateInUse(checked)
+ confirmModal?.update({ okButtonProps: { disabled: checked } })
+ await handleChangeInUse(catalog, checked)
+ }
+
+ return (
+ <Flex vertical gap={8}>
+ {initialInUse ? (
+ <>
+ <Paragraph style={{ marginBottom: 0 }}>
+ Please set the {type} to "Not In-Use" before
deleting.
+ </Paragraph>
+ <Flex align='center' gap={8}>
+ <Switch size='small' checked={inUse}
onChange={handleSwitchChange} />
+ <span>{inUse ? 'In-Use' : 'Not In-Use'}</span>
+ </Flex>
+ </>
+ ) : (
<ConfirmInput
- name={name}
+ name={catalog.name}
type={type}
setConfirmInput={setConfirmInput}
registerValidate={registerValidate}
/>
)}
- </NameContext.Consumer>
- ),
+ </Flex>
+ )
+ }
+
+ confirmModal = modal.confirm({
+ title: `Are you sure to delete the ${type} ${catalog.name}?`,
+ icon: <ExclamationCircleFilled />,
+ content: <DeleteCatalogConfirmContent />,
+ okButtonProps: { disabled: currentInUse },
okText: 'Delete',
okType: 'danger',
cancelText: 'Cancel',
onOk(close) {
+ if (currentInUse) return Promise.reject()
if (validateFn && !validateFn()) return
const confirmFn = async () => {
@@ -252,11 +275,8 @@ export default function CatalogsPage() {
key: 'action',
width: 100,
render: (_, record) => {
- const NameContext = createContext(record.name)
-
return (
<div className='flex gap-2'>
- <NameContext.Provider
value={record.name}>{contextHolder}</NameContext.Provider>
<a data-refer={`edit-entity-${record.name}`}>
<Tooltip title='Edit'>
<Icons.Pencil className='size-4' onClick={() =>
handleEditCatalog(record.name)} />
@@ -264,10 +284,7 @@ export default function CatalogsPage() {
</a>
<a data-refer={`delete-entity-${record.name}`}>
<Tooltip title='Delete'>
- <Icons.Trash2Icon
- className='size-4'
- onClick={() => showDeleteConfirm(NameContext, record,
'catalog')}
- />
+ <Icons.Trash2Icon className='size-4' onClick={() =>
showDeleteConfirm(record, 'catalog')} />
</Tooltip>
</a>
<Dropdown
@@ -324,12 +341,19 @@ export default function CatalogsPage() {
}, [columns])
const handleChangeInUse = async (catalogObj, checked) => {
- await dispatch(switchInUseCatalog({ metalake: currentMetalake, catalog:
catalogObj.name, isInUse: checked }))
+ const action = await dispatch(
+ switchInUseCatalog({ metalake: currentMetalake, catalog:
catalogObj.name, isInUse: checked })
+ )
+ if (!action?.meta || action.meta.requestStatus !== 'fulfilled') {
+ return
+ }
+ dispatch(updateCatalogInUse({ catalog: catalogObj.name, isInUse: checked
}))
treeRef.current?.setCatalogInUse(catalogObj.name, checked)
}
return (
<div>
+ {contextHolder}
<Title level={2}>Catalogs</Title>
<Paragraph type='secondary'>This table lists the data catalogs you have
access to.</Paragraph>
<Flex justify='flex-end' className='mb-4'>
diff --git a/web-v2/web/src/lib/store/metalakes/index.js
b/web-v2/web/src/lib/store/metalakes/index.js
index 2d658023ca..6a77e58777 100644
--- a/web-v2/web/src/lib/store/metalakes/index.js
+++ b/web-v2/web/src/lib/store/metalakes/index.js
@@ -695,8 +695,6 @@ export const switchInUseCatalog = createAsyncThunk(
throw new Error(err)
}
- dispatch(fetchCatalogs({ metalake, init: true }))
-
return res
}
)
@@ -1769,6 +1767,26 @@ export const appMetalakesSlice = createSlice({
const { key, data } = action.payload
state.metalakeTree = updateTreeData(state.metalakeTree, key, data)
},
+ updateCatalogInUse(state, action) {
+ const { catalog, isInUse } = action.payload
+ const inUseValue = isInUse ? 'true' : 'false'
+ state.tableData = state.tableData.map(item => {
+ if (item.name !== catalog) return item
+
+ return {
+ ...item,
+ properties: { ...(item.properties || {}), 'in-use': inUseValue }
+ }
+ })
+ state.catalogs = state.catalogs.map(item => {
+ if (item.name !== catalog) return item
+
+ return {
+ ...item,
+ properties: { ...(item.properties || {}), 'in-use': inUseValue }
+ }
+ })
+ },
addCatalogToTree(state, action) {
const catalogIndex = state.metalakeTree.findIndex(c => c.key ===
action.payload.key)
if (catalogIndex === -1) {
@@ -2209,6 +2227,7 @@ export const {
setExpandedNodes,
addCatalogToTree,
setCatalogInUse,
+ updateCatalogInUse,
setMetalakeInUse,
removeCatalogFromTree,
setTableProps,