This is an automated email from the ASF dual-hosted git repository.

rusackas pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new c0e5f5226d4 fix(explore): let chart owners overwrite when owners come 
through as objects (#41352)
c0e5f5226d4 is described below

commit c0e5f5226d493324dccf8fc6ec1a847dbb452f3e
Author: Evan Rusackas <[email protected]>
AuthorDate: Mon Jul 13 03:10:00 2026 -0700

    fix(explore): let chart owners overwrite when owners come through as 
objects (#41352)
    
    Co-authored-by: Claude Opus 4.8 <[email protected]>
---
 .../src/explore/components/SaveModal.test.tsx      | 21 +++++++++++++++++++++
 .../src/explore/components/SaveModal.tsx           | 22 ++++++++++++++++++++--
 2 files changed, 41 insertions(+), 2 deletions(-)

diff --git a/superset-frontend/src/explore/components/SaveModal.test.tsx 
b/superset-frontend/src/explore/components/SaveModal.test.tsx
index 5538bd76f27..20c3c7977dd 100644
--- a/superset-frontend/src/explore/components/SaveModal.test.tsx
+++ b/superset-frontend/src/explore/components/SaveModal.test.tsx
@@ -358,6 +358,27 @@ test('enables overwrite option for admin non-editor', () 
=> {
   expect(getByRole('radio', { name: 'Save (Overwrite)' })).toBeEnabled();
 });
 
+test('enables overwrite option for owner when owners are objects', () => {
+  // The Slice type declares `owners` as `number[]`, but the explore
+  // bootstrap can deliver owners as `{ id: number }` objects at runtime.
+  // A chart owner must still be able to overwrite their own chart in that 
case.
+  const { getByRole } = setup(
+    {},
+    mockStore({
+      ...initialState,
+      explore: {
+        ...initialState.explore,
+        slice: {
+          ...initialState.explore.slice,
+          owners: [{ id: 1 }],
+        },
+      },
+      user: { userId: 1 },
+    }),
+  );
+  expect(getByRole('radio', { name: 'Save (Overwrite)' })).toBeEnabled();
+});
+
 test('updates slice name and selected dashboard', async () => {
   const dashboardId = mockEvent.value;
   const saveDataset = jest.fn().mockResolvedValue(undefined);
diff --git a/superset-frontend/src/explore/components/SaveModal.tsx 
b/superset-frontend/src/explore/components/SaveModal.tsx
index 62ce1b47ea7..1d0bf9e2740 100755
--- a/superset-frontend/src/explore/components/SaveModal.tsx
+++ b/superset-frontend/src/explore/components/SaveModal.tsx
@@ -228,6 +228,21 @@ const SaveModal = ({
   const history = useHistory();
   const theme = useTheme();
 
+  const isCurrentUserOwner = useCallback((): boolean => {
+    const userId = user?.userId;
+    if (userId === undefined) {
+      return false;
+    }
+    // The Slice type declares `owners` as `number[]`, but the explore
+    // bootstrap payload can carry owners as API `Owner` objects (`{ id }`)
+    // instead, so normalize to the numeric id before comparing.
+    return Boolean(
+      slice?.owners?.some((owner: number | { id?: number }) =>
+        typeof owner === 'number' ? owner === userId : owner?.id === userId,
+      ),
+    );
+  }, [slice, user]);
+
   const canOverwriteSlice = useCallback((): boolean => {
     const userSubjects = getBootstrapData()?.common?.user_subjects ?? [];
     const canEditSlice = Boolean(
@@ -238,10 +253,13 @@ const SaveModal = ({
 
     return (
       !!slice &&
-      (can_overwrite || isUserAdmin(user) || canEditSlice) &&
+      (can_overwrite ||
+        isUserAdmin(user) ||
+        canEditSlice ||
+        isCurrentUserOwner()) &&
       !slice?.is_managed_externally
     );
-  }, [can_overwrite, slice, user]);
+  }, [can_overwrite, slice, user, isCurrentUserOwner]);
 
   const [newSliceName, setNewSliceName] = useState<string | undefined>(
     sliceName,

Reply via email to