diff --git a/csharp/App/Backend/DataTypes/Methods/Session.cs b/csharp/App/Backend/DataTypes/Methods/Session.cs
index 5a3edd808..851a5fdca 100644
--- a/csharp/App/Backend/DataTypes/Methods/Session.cs
+++ b/csharp/App/Backend/DataTypes/Methods/Session.cs
@@ -375,7 +375,8 @@ public static class SessionMethods
var emailOwner = Db.GetUserByEmail(editedUser.Email);
return sessionUser.UserType != 0
- && originalUser.Id != 0 // never edit the root user
+ && originalUser.Id != 0 // belt: legacy root-id sentinel
+ && originalUser.ParentId > 0 // never edit the main/root admin (parentless top user; ParentId<=0, Id NOT necessarily 0)
&& (sessionUser.UserType == 2 || sessionUser.HasAccessTo(originalUser)) // admins may edit any user
&& (emailOwner is null || emailOwner.Id == editedUser.Id) // email not taken by another user
&& editedUser
@@ -402,8 +403,9 @@ public static class SessionMethods
return sessionUser is not null
&& userToDelete is not null
&& sessionUser.UserType !=0
- && userToDelete.Id != 0 // never delete the root user
- && userToDelete.Id != sessionUser.Id // never self-delete (avoid lockout)
+ && userToDelete.Id != 0 // belt: legacy root-id sentinel
+ && userToDelete.ParentId > 0 // never delete the main/root admin (the parentless top user; ParentId<=0, Id NOT necessarily 0)
+ && userToDelete.Id != sessionUser.Id // never self-delete (avoid lockout)
&& (sessionUser.UserType == 2 || sessionUser.HasAccessTo(userToDelete)) // admins may delete any user
&& Db.Delete(userToDelete);
}
diff --git a/typescript/frontend-marios2/src/content/dashboards/Users/User.tsx b/typescript/frontend-marios2/src/content/dashboards/Users/User.tsx
index 074c1c2bd..bec15752a 100644
--- a/typescript/frontend-marios2/src/content/dashboards/Users/User.tsx
+++ b/typescript/frontend-marios2/src/content/dashboards/Users/User.tsx
@@ -16,6 +16,7 @@ import {
Tab,
Tabs,
TextField,
+ Tooltip,
Typography,
useTheme
} from '@mui/material';
@@ -24,6 +25,7 @@ import Button from '@mui/material/Button';
import axiosConfig from 'src/Resources/axiosConfig';
import { InnovEnergyUser } from 'src/interfaces/UserTypes';
import { TokenContext } from 'src/contexts/tokenContext';
+import { UserContext } from 'src/contexts/userContext';
import { TabsContainerWrapper } from 'src/layouts/TabsContainerWrapper';
import { FormattedMessage, useIntl } from 'react-intl';
import UserAccess from '../ManageAccess/UserAccess';
@@ -43,6 +45,8 @@ function User(props: singleUserProps) {
const [formValues, setFormValues] = useState(props.current_user);
const tokencontext = useContext(TokenContext);
const { removeToken } = tokencontext;
+ const userContext = useContext(UserContext);
+ const loggedInUser = userContext?.currentUser;
const tabs = [
{ value: 'user', label: intl.formatMessage({ id: 'user' }) },
{ value: 'manage', label: intl.formatMessage({ id: 'accessManagement' }) }
@@ -161,6 +165,17 @@ function User(props: singleUserProps) {
const isMobile = window.innerWidth <= 1490;
+ // Mirror the backend delete guards: the main/root admin (the parentless top
+ // user — parentId 0; its id is NOT necessarily 0) and your own account can
+ // never be deleted, so disable the button and explain why.
+ const isMainAdmin = formValues.parentId <= 0 || formValues.id === 0;
+ const deleteDisabledReason = isMainAdmin
+ ? intl.formatMessage({ id: 'cannotDeleteMainAdmin' })
+ : loggedInUser?.id === formValues.id
+ ? intl.formatMessage({ id: 'cannotDeleteSelf' })
+ : '';
+ const cannotDelete = deleteDisabledReason !== '';
+
return (
<>
{openModalDeleteUser && (
@@ -350,18 +365,20 @@ function User(props: singleUserProps) {
defaultMessage="Apply Changes"
/>
-
+
+
+
+
+
{loading && (