mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-03-11 02:12:21 -05:00
Wired invites list in admin-x settings
refs https://github.com/TryGhost/Team/issues/3351 - adds api to get list of all invites on the site - wires invite list with Revoke/Resend option on the User settings group
This commit is contained in:
parent
1a0c1c0cd2
commit
b307c59d54
5 changed files with 100 additions and 5 deletions
|
@ -1,9 +1,11 @@
|
|||
import React, {createContext, useCallback, useContext, useEffect, useState} from 'react';
|
||||
import {ServicesContext} from './ServiceProvider';
|
||||
import {User} from '../../types/api';
|
||||
import {UserInvite} from '../../utils/api';
|
||||
|
||||
interface UsersContextProps {
|
||||
users: User[];
|
||||
invites: UserInvite[];
|
||||
currentUser: User|null;
|
||||
updateUser?: (user: User) => Promise<void>;
|
||||
}
|
||||
|
@ -14,12 +16,14 @@ interface UsersProviderProps {
|
|||
|
||||
const UsersContext = createContext<UsersContextProps>({
|
||||
users: [],
|
||||
invites: [],
|
||||
currentUser: null
|
||||
});
|
||||
|
||||
const UsersProvider: React.FC<UsersProviderProps> = ({children}) => {
|
||||
const {api} = useContext(ServicesContext);
|
||||
const [users, setUsers] = useState <User[]> ([]);
|
||||
const [invites, setInvites] = useState <UserInvite[]> ([]);
|
||||
const [currentUser, setCurrentUser] = useState <User|null> (null);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -28,8 +32,10 @@ const UsersProvider: React.FC<UsersProviderProps> = ({children}) => {
|
|||
// get list of staff users from the API
|
||||
const data = await api.users.browse();
|
||||
const user = await api.users.currentUser();
|
||||
const invitesRes = await api.invites.browse();
|
||||
setUsers(data.users);
|
||||
setCurrentUser(user);
|
||||
setInvites(invitesRes.invites);
|
||||
} catch (error) {
|
||||
// Log error in API
|
||||
}
|
||||
|
@ -58,6 +64,7 @@ const UsersProvider: React.FC<UsersProviderProps> = ({children}) => {
|
|||
return (
|
||||
<UsersContext.Provider value={{
|
||||
users,
|
||||
invites,
|
||||
currentUser,
|
||||
updateUser
|
||||
}}>
|
||||
|
|
|
@ -11,6 +11,7 @@ import TabView from '../../../admin-x-ds/global/TabView';
|
|||
import UserDetailModal from './modals/UserDetailModal';
|
||||
import useStaffUsers from '../../../hooks/useStaffUsers';
|
||||
import {User} from '../../../types/api';
|
||||
import {UserInvite} from '../../../utils/api';
|
||||
import {generateAvatarColor, getInitials} from '../../../utils/helpers';
|
||||
|
||||
interface OwnerProps {
|
||||
|
@ -23,6 +24,11 @@ interface UsersListProps {
|
|||
updateUser?: (user: User) => void;
|
||||
}
|
||||
|
||||
interface InviteListProps {
|
||||
users: UserInvite[];
|
||||
updateUser?: (user: User) => void;
|
||||
}
|
||||
|
||||
const Owner: React.FC<OwnerProps> = ({user, updateUser}) => {
|
||||
const showDetailModal = () => {
|
||||
NiceModal.show(UserDetailModal, {user, updateUser});
|
||||
|
@ -67,7 +73,7 @@ const UsersList: React.FC<UsersListProps> = ({users, updateUser}) => {
|
|||
detail={user.email}
|
||||
hideActions={true}
|
||||
id={`list-item-${user.id}`}
|
||||
title={user.name}
|
||||
title={user.name || ''}
|
||||
onClick={() => showDetailModal(user)} />
|
||||
);
|
||||
})}
|
||||
|
@ -75,6 +81,48 @@ const UsersList: React.FC<UsersListProps> = ({users, updateUser}) => {
|
|||
);
|
||||
};
|
||||
|
||||
const InvitesUserList: React.FC<InviteListProps> = ({users}) => {
|
||||
if (!users || !users.length) {
|
||||
return (
|
||||
<NoValueLabel icon='single-user-neutral-block'>
|
||||
No users found.
|
||||
</NoValueLabel>
|
||||
);
|
||||
}
|
||||
|
||||
const actions = (
|
||||
<div className='flex'>
|
||||
<Button color='red' label='Revoke' link={true} onClick={() => {
|
||||
// Revoke invite
|
||||
}}/>
|
||||
<Button className='ml-2' color='green' label='Resend' link={true} onClick={() => {
|
||||
// Resend invite
|
||||
}}/>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<List>
|
||||
{users.map((user) => {
|
||||
return (
|
||||
<ListItem
|
||||
key={user.id}
|
||||
action={actions}
|
||||
avatar={(<Avatar bgColor={generateAvatarColor((user.email))} image={''} label={''} labelColor='white' />)}
|
||||
detail={user.role}
|
||||
hideActions={true}
|
||||
id={`list-item-${user.id}`}
|
||||
title={user.email}
|
||||
onClick={() => {
|
||||
// do nothing
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
);
|
||||
};
|
||||
|
||||
const Users: React.FC = () => {
|
||||
const {
|
||||
ownerUser,
|
||||
|
@ -82,6 +130,7 @@ const Users: React.FC = () => {
|
|||
editorUsers,
|
||||
authorUsers,
|
||||
contributorUsers,
|
||||
invites,
|
||||
updateUser
|
||||
} = useStaffUsers();
|
||||
|
||||
|
@ -119,7 +168,7 @@ const Users: React.FC = () => {
|
|||
{
|
||||
id: 'users-invited',
|
||||
title: 'Invited',
|
||||
contents: (<></>)
|
||||
contents: (<InvitesUserList updateUser={updateUser} users={invites} />)
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import {RolesContext} from '../components/providers/RolesProvider';
|
||||
import {User} from '../types/api';
|
||||
import { UserInvite } from '../utils/api';
|
||||
import {UsersContext} from '../components/providers/UsersProvider';
|
||||
import {useContext} from 'react';
|
||||
|
||||
export type UsersHook = {
|
||||
users: User[];
|
||||
invites: UserInvite[];
|
||||
ownerUser: User;
|
||||
adminUsers: User[];
|
||||
editorUsers: User[];
|
||||
|
@ -26,13 +29,22 @@ function getOwnerUser(users: User[]): User {
|
|||
}
|
||||
|
||||
const useStaffUsers = (): UsersHook => {
|
||||
const {users, currentUser, updateUser} = useContext(UsersContext);
|
||||
const {users, currentUser, updateUser, invites} = useContext(UsersContext);
|
||||
const {roles} = useContext(RolesContext);
|
||||
const ownerUser = getOwnerUser(users);
|
||||
const adminUsers = getUsersByRole(users, 'Administrator');
|
||||
const editorUsers = getUsersByRole(users, 'Editor');
|
||||
const authorUsers = getUsersByRole(users, 'Author');
|
||||
const contributorUsers = getUsersByRole(users, 'Contributor');
|
||||
|
||||
const mappedInvites = invites?.map((invite) => {
|
||||
let role = roles.find((r) => {
|
||||
return invite.role_id === r.id;
|
||||
});
|
||||
return {
|
||||
...invite,
|
||||
role: role?.name
|
||||
};
|
||||
});
|
||||
return {
|
||||
users,
|
||||
ownerUser,
|
||||
|
@ -41,6 +53,7 @@ const useStaffUsers = (): UsersHook => {
|
|||
authorUsers,
|
||||
contributorUsers,
|
||||
currentUser,
|
||||
invites: mappedInvites,
|
||||
updateUser
|
||||
};
|
||||
};
|
||||
|
|
|
@ -27,6 +27,22 @@ export interface RolesResponseType {
|
|||
roles: UserRole[];
|
||||
}
|
||||
|
||||
export interface UserInvite {
|
||||
created_at: string;
|
||||
email: string;
|
||||
expires: string;
|
||||
id: string;
|
||||
role_id: string;
|
||||
role?: string;
|
||||
status: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface InvitesResponseType {
|
||||
meta?: Meta;
|
||||
invites: UserInvite[];
|
||||
}
|
||||
|
||||
export interface SiteResponseType {
|
||||
site: SiteData;
|
||||
}
|
||||
|
@ -79,6 +95,9 @@ interface API {
|
|||
images: {
|
||||
upload: ({file}: {file: File}) => Promise<ImagesResponseType>;
|
||||
};
|
||||
invites: {
|
||||
browse: () => Promise<InvitesResponseType>;
|
||||
}
|
||||
}
|
||||
|
||||
interface GhostApiOptions {
|
||||
|
@ -203,6 +222,13 @@ function setupGhostApi({ghostVersion}: GhostApiOptions): API {
|
|||
const data: any = await response.json();
|
||||
return data;
|
||||
}
|
||||
},
|
||||
invites: {
|
||||
browse: async () => {
|
||||
const response = await fetcher(`/invites/`, {});
|
||||
const data: InvitesResponseType = await response.json();
|
||||
return data;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ export function getOptionLabel(
|
|||
return options?.find(option => option.value === value)?.label;
|
||||
}
|
||||
|
||||
export function getInitials(name: string) {
|
||||
export function getInitials(name: string = '') {
|
||||
let rgx = new RegExp(/(\p{L}{1})\p{L}+/, 'gu');
|
||||
let rgxInitials = [...name.matchAll(rgx)] || [];
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue