diff --git a/ghost/admin-x-settings/src/components/settings/general/Users.tsx b/ghost/admin-x-settings/src/components/settings/general/Users.tsx index 75f7728291..60fcecc7db 100644 --- a/ghost/admin-x-settings/src/components/settings/general/Users.tsx +++ b/ghost/admin-x-settings/src/components/settings/general/Users.tsx @@ -13,9 +13,19 @@ import useStaffUsers from '../../../hooks/useStaffUsers'; import {User} from '../../../types/api'; import {generateAvatarColor, getInitials} from '../../../utils/helpers'; -const Owner: React.FC<{user: User}> = ({user}) => { +interface OwnerProps { + user: User; + updateUser?: (user: User) => void; +} + +interface UsersListProps { + users: User[]; + updateUser?: (user: User) => void; +} + +const Owner: React.FC = ({user, updateUser}) => { const showDetailModal = () => { - NiceModal.show(UserDetailModal, {user}); + NiceModal.show(UserDetailModal, {user, updateUser}); }; if (!user) { @@ -33,11 +43,6 @@ const Owner: React.FC<{user: User}> = ({user}) => { ); }; -interface UsersListProps { - users: User[]; - updateUser?: (user: User) => void; -} - const UsersList: React.FC = ({users, updateUser}) => { const showDetailModal = (user: User) => { NiceModal.show(UserDetailModal, {user, updateUser}); @@ -123,7 +128,7 @@ const Users: React.FC = () => { customButtons={buttons} title='Users and permissions' > - + ); diff --git a/ghost/admin-x-settings/src/components/settings/general/modals/InviteUserModal.tsx b/ghost/admin-x-settings/src/components/settings/general/modals/InviteUserModal.tsx index 6383aca83b..0e76c577b5 100644 --- a/ghost/admin-x-settings/src/components/settings/general/modals/InviteUserModal.tsx +++ b/ghost/admin-x-settings/src/components/settings/general/modals/InviteUserModal.tsx @@ -3,11 +3,11 @@ import NiceModal from '@ebay/nice-modal-react'; const InviteUserModal = NiceModal.create(() => { return ( - { - alert('Clicked OK'); + // Handle invite user }} >
diff --git a/ghost/admin-x-settings/src/components/settings/general/modals/UserDetailModal.tsx b/ghost/admin-x-settings/src/components/settings/general/modals/UserDetailModal.tsx index dc72354c2e..16e752294d 100644 --- a/ghost/admin-x-settings/src/components/settings/general/modals/UserDetailModal.tsx +++ b/ghost/admin-x-settings/src/components/settings/general/modals/UserDetailModal.tsx @@ -4,14 +4,14 @@ import Heading from '../../../../admin-x-ds/global/Heading'; import Modal from '../../../../admin-x-ds/global/Modal'; import NiceModal from '@ebay/nice-modal-react'; import Radio from '../../../../admin-x-ds/global/Radio'; -import React, {useState} from 'react'; +import React, {useEffect, useState} from 'react'; import SettingGroup from '../../../../admin-x-ds/settings/SettingGroup'; import SettingGroupContent from '../../../../admin-x-ds/settings/SettingGroupContent'; import TextField from '../../../../admin-x-ds/global/TextField'; import Toggle from '../../../../admin-x-ds/global/Toggle'; import useRoles from '../../../../hooks/useRoles'; import {User} from '../../../../types/api'; -import {generateAvatarColor, getInitials} from '../../../../utils/helpers'; +import {generateAvatarColor, getInitials, isOwnerUser} from '../../../../utils/helpers'; interface CustomHeadingProps { children?: React.ReactNode; @@ -27,8 +27,57 @@ const CustomHeader: React.FC = ({children}) => { {children} ); }; -const BasicInputs: React.FC = ({user, setUserData}) => { + +const RoleSelector: React.FC = ({user, setUserData}) => { const {roles} = useRoles(); + if (isOwnerUser(user)) { + return ( + <> + Role +
+ This user is the owner of the site. To change their role, you need to transfer the ownership first. +
+ + ); + } + + return ( + { + const role = roles?.find(r => r.name.toLowerCase() === value.toLowerCase()); + if (role) { + setUserData?.({...user, roles: [role]}); + } + }} + /> + ); +}; +const BasicInputs: React.FC = ({user, setUserData}) => { return ( = ({user, setUserData}) => { setUserData?.({...user, email: e.target.value}); }} /> - { - const role = roles?.find(r => r.name.toLowerCase() === value.toLowerCase()); - if (role) { - setUserData?.({...user, roles: [role]}); - } - }} - /> + ); }; @@ -95,52 +112,70 @@ const Basic: React.FC = ({user, setUserData}) => { ); }; -const DetailsInputs: React.FC = ({user}) => { +const DetailsInputs: React.FC = ({user, setUserData}) => { return ( { + setUserData?.({...user, slug: e.target.value}); + }} /> { + setUserData?.({...user, location: e.target.value}); + }} /> { + setUserData?.({...user, website: e.target.value}); + }} /> { + setUserData?.({...user, facebook: e.target.value}); + }} /> { + setUserData?.({...user, twitter: e.target.value}); + }} /> { + setUserData?.({...user, bio: e.target.value}); + }} /> ); }; -const Details: React.FC = ({user}) => { +const Details: React.FC = ({user, setUserData}) => { return ( Details} title='Details' > - + ); }; -const EmailNotificationsInputs: React.FC = ({user}) => { +const EmailNotificationsInputs: React.FC = ({user, setUserData}) => { return ( = ({user}) => { hint='Every time a member comments on one of your posts' id='comments' label='Comments' + onChange={(e) => { + setUserData?.({...user, comment_notifications: e.target.checked}); + }} /> = ({user}) => { hint='Every time a new free member signs up' id='new-signups' label='New signups' + onChange={(e) => { + setUserData?.({...user, free_member_signup_notification: e.target.checked}); + }} /> = ({user}) => { hint='Every time a member starts a new paid subscription' id='new-paid-members' label='New paid members' + onChange={(e) => { + setUserData?.({...user, paid_subscription_started_notification: e.target.checked}); + }} /> = ({user}) => { hint='Every time a member cancels their paid subscription' id='paid-member-cancellations' label='Paid member cancellations' + onChange={(e) => { + setUserData?.({...user, paid_subscription_canceled_notification: e.target.checked}); + }} /> = ({user}) => { hint='Occasional summaries of your audience & revenue growth' id='milestones' label='Milestones' + onChange={(e) => { + setUserData?.({...user, milestone_notifications: e.target.checked}); + }} /> ); }; -const EmailNotifications: React.FC = ({user}) => { +const EmailNotifications: React.FC = ({user, setUserData}) => { return ( Email notifications} title='Email notifications' + > - + ); }; @@ -242,14 +293,31 @@ interface UserDetailModalProps { const UserDetailModal:React.FC = ({user, updateUser}) => { const [userData, setUserData] = useState(user); + const [saveState, setSaveState] = useState(''); + + let okLabel = saveState === 'saved' ? 'Saved' : 'Save'; + if (saveState === 'saving') { + okLabel = 'Saving...'; + } + + // remove saved state after 2 seconds + useEffect(() => { + if (saveState === 'saved') { + setTimeout(() => { + setSaveState(''); + }, 2000); + } + }, [saveState]); + return ( { - alert('Clicked OK'); - updateUser?.(userData); + onOk={async () => { + setSaveState('saving'); + await updateUser?.(userData); + setSaveState('saved'); }} >
@@ -267,8 +335,8 @@ const UserDetailModal:React.FC = ({user, updateUser}) => {
-
- +
+
diff --git a/ghost/admin-x-settings/src/utils/helpers.ts b/ghost/admin-x-settings/src/utils/helpers.ts index c3622c8862..0b8411e8dc 100644 --- a/ghost/admin-x-settings/src/utils/helpers.ts +++ b/ghost/admin-x-settings/src/utils/helpers.ts @@ -1,4 +1,4 @@ -import {Setting, SettingValue} from '../types/api'; +import {Setting, SettingValue, User} from '../types/api'; export interface IGhostPaths { adminRoot: string; @@ -57,4 +57,8 @@ export function generateAvatarColor(name: string) { const h = hash % 360; return 'hsl(' + h + ', ' + s + '%, ' + l + '%)'; -} \ No newline at end of file +} + +export function isOwnerUser(user: User) { + return user.roles.some(role => role.name === 'Owner'); +}