diff --git a/apps/admin-x-settings/src/components/settings/general/UserDetailModal.tsx b/apps/admin-x-settings/src/components/settings/general/UserDetailModal.tsx
index 4fcc120d74..feb9a7a0b3 100644
--- a/apps/admin-x-settings/src/components/settings/general/UserDetailModal.tsx
+++ b/apps/admin-x-settings/src/components/settings/general/UserDetailModal.tsx
@@ -8,7 +8,7 @@ import Menu, {MenuItem} from '../../../admin-x-ds/global/Menu';
import Modal from '../../../admin-x-ds/global/modal/Modal';
import NiceModal, {useModal} from '@ebay/nice-modal-react';
import Radio from '../../../admin-x-ds/global/form/Radio';
-import React, {useEffect, useRef, useState} from 'react';
+import React, {useCallback, useEffect, useRef, useState} from 'react';
import SettingGroup from '../../../admin-x-ds/settings/SettingGroup';
import SettingGroupContent from '../../../admin-x-ds/settings/SettingGroupContent';
import TextArea from '../../../admin-x-ds/global/form/TextArea';
@@ -21,7 +21,7 @@ import useStaffUsers from '../../../hooks/useStaffUsers';
import validator from 'validator';
import {HostLimitError, useLimiter} from '../../../hooks/useLimiter';
import {RoutingModalProps} from '../../providers/RoutingProvider';
-import {User, isAdminUser, isOwnerUser, useDeleteUser, useEditUser, useMakeOwner, useUpdatePassword} from '../../../api/users';
+import {User, canAccessSettings, hasAdminAccess, isAdminUser, isOwnerUser, useDeleteUser, useEditUser, useMakeOwner, useUpdatePassword} from '../../../api/users';
import {genStaffToken, getStaffToken} from '../../../api/staffToken';
import {getImageUrl, useUploadImage} from '../../../api/images';
import {getSettingValues} from '../../../api/settings';
@@ -108,6 +108,8 @@ const RoleSelector: React.FC
= ({user, setUserData}) => {
};
const BasicInputs: React.FC = ({errors, validators, user, setUserData}) => {
+ const {currentUser} = useGlobalData();
+
return (
= ({errors, validators, user, setUs
setUserData?.({...user, email: e.target.value});
}}
/>
-
+ {hasAdminAccess(currentUser) && }
);
};
@@ -227,6 +229,7 @@ const Details: React.FC = ({errors, validators, user, setUserDa
const EmailNotificationsInputs: React.FC = ({user, setUserData}) => {
const hasWebmentions = useFeatureFlag('webmentions');
+ const {currentUser} = useGlobalData();
return (
@@ -239,51 +242,53 @@ const EmailNotificationsInputs: React.FC = ({user, setUserData}
setUserData?.({...user, comment_notifications: e.target.checked});
}}
/>
- {hasWebmentions && {
- setUserData?.({...user, mention_notifications: e.target.checked});
- }}
- />}
- {
- setUserData?.({...user, free_member_signup_notification: e.target.checked});
- }}
- />
- {
- setUserData?.({...user, paid_subscription_started_notification: e.target.checked});
- }}
- />
- {
- setUserData?.({...user, paid_subscription_canceled_notification: e.target.checked});
- }}
- />
- {
- setUserData?.({...user, milestone_notifications: e.target.checked});
- }}
- />
+ {hasAdminAccess(currentUser) && <>
+ {hasWebmentions && {
+ setUserData?.({...user, mention_notifications: e.target.checked});
+ }}
+ />}
+ {
+ setUserData?.({...user, free_member_signup_notification: e.target.checked});
+ }}
+ />
+ {
+ setUserData?.({...user, paid_subscription_started_notification: e.target.checked});
+ }}
+ />
+ {
+ setUserData?.({...user, paid_subscription_canceled_notification: e.target.checked});
+ }}
+ />
+ {
+ setUserData?.({...user, milestone_notifications: e.target.checked});
+ }}
+ />
+ >}
);
};
@@ -439,7 +444,7 @@ const StaffToken: React.FC = () => {
const [token, setToken] = useState('');
const {mutateAsync: newApiKey} = genStaffToken();
const [copied, setCopied] = useState(false);
-
+
const copyToClipboard = () => {
navigator.clipboard.writeText(token);
setCopied(true);
@@ -496,6 +501,7 @@ const UserMenuTrigger = () => (
const UserDetailModalContent: React.FC<{user: User}> = ({user}) => {
const {updateRoute} = useRouting();
const {ownerUser} = useStaffUsers();
+ const {currentUser} = useGlobalData();
const [userData, _setUserData] = useState(user);
const [saveState, setSaveState] = useState<'' | 'unsaved' | 'saving' | 'saved'>('');
const [errors, setErrors] = useState<{
@@ -531,14 +537,22 @@ const UserDetailModalContent: React.FC<{user: User}> = ({user}) => {
disabled: !pinturaEnabled}
);
+ const navigateOnClose = useCallback(() => {
+ if (canAccessSettings(currentUser)) {
+ updateRoute('users');
+ } else {
+ updateRoute({isExternal: true, route: 'dashboard'});
+ }
+ }, [currentUser, updateRoute]);
+
useEffect(() => {
if (saveState === 'saved') {
setTimeout(() => {
mainModal.remove();
- updateRoute('users');
+ navigateOnClose();
}, 300);
}
- }, [mainModal, saveState, updateRoute]);
+ }, [mainModal, navigateOnClose, saveState, updateRoute]);
const confirmSuspend = async (_user: User) => {
if (_user.status === 'inactive' && _user.roles[0].name !== 'Contributor') {
@@ -741,10 +755,12 @@ const UserDetailModalContent: React.FC<{user: User}> = ({user}) => {
return (
updateRoute('users')}
+ afterClose={navigateOnClose}
+ animate={canAccessSettings(currentUser)}
+ backDrop={canAccessSettings(currentUser)}
dirty={saveState === 'unsaved'}
okLabel={okLabel}
- size='lg'
+ size={canAccessSettings(currentUser) ? 'lg' : 'full'}
stickyFooter={true}
testId='user-detail-modal'
onOk={async () => {
diff --git a/apps/admin-x-settings/src/components/settings/general/Users.tsx b/apps/admin-x-settings/src/components/settings/general/Users.tsx
index b0b0e35ab4..0f43419461 100644
--- a/apps/admin-x-settings/src/components/settings/general/Users.tsx
+++ b/apps/admin-x-settings/src/components/settings/general/Users.tsx
@@ -6,12 +6,14 @@ import NoValueLabel from '../../../admin-x-ds/global/NoValueLabel';
import React, {useState} from 'react';
import SettingGroup from '../../../admin-x-ds/settings/SettingGroup';
import TabView from '../../../admin-x-ds/global/TabView';
+import clsx from 'clsx';
import useRouting from '../../../hooks/useRouting';
import useStaffUsers from '../../../hooks/useStaffUsers';
-import {User} from '../../../api/users';
+import {User, hasAdminAccess, isContributorUser, isEditorUser} from '../../../api/users';
import {UserInvite, useAddInvite, useDeleteInvite} from '../../../api/invites';
import {generateAvatarColor, getInitials} from '../../../utils/helpers';
import {showToast} from '../../../admin-x-ds/global/Toast';
+import {useGlobalData} from '../../providers/GlobalDataProvider';
interface OwnerProps {
user: User;
@@ -31,9 +33,12 @@ interface InviteListProps {
const Owner: React.FC = ({user}) => {
const {updateRoute} = useRouting();
+ const {currentUser} = useGlobalData();
const showDetailModal = () => {
- updateRoute({route: `users/show/${user.slug}`});
+ if (hasAdminAccess(currentUser)) {
+ updateRoute({route: `users/show/${user.slug}`});
+ }
};
if (!user) {
@@ -41,10 +46,10 @@ const Owner: React.FC = ({user}) => {
}
return (
-
+
- {user.name} — Owner
+ {user.name} — Owner {hasAdminAccess(currentUser) && }
{user.email}
@@ -53,6 +58,7 @@ const Owner: React.FC
= ({user}) => {
const UsersList: React.FC = ({users, groupname}) => {
const {updateRoute} = useRouting();
+ const {currentUser} = useGlobalData();
const showDetailModal = (user: User) => {
updateRoute({route: `users/show/${user.slug}`});
@@ -73,11 +79,17 @@ const UsersList: React.FC = ({users, groupname}) => {
if (user.status === 'inactive') {
title = `${title} (Suspended)`;
}
+
+ const canEdit = hasAdminAccess(currentUser) ||
+ (isEditorUser(currentUser) && isContributorUser(user)) ||
+ currentUser.id === user.id;
+
return (
showDetailModal(user)}/>}
+ action={canEdit &&
- {{#if (gh-user-can-admin this.session.user)}}
+ {{#if (or (gh-user-can-admin this.session.user) this.session.user.isEditor)}}
{{#if (feature "adminXSettings")}}
{{svg-jar "settings"}}
{{else}}
{{svg-jar "settings"}}
{{/if}}
{{/if}}
- {{#if this.session.user.isEditor}}
-
{{svg-jar "settings"}}
- {{/if}}