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 &quot;Not In-Use&quot; 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,

Reply via email to