diff --git a/ghost/admin-x-settings/src/App.tsx b/ghost/admin-x-settings/src/App.tsx
index f903692de1..0daaaff96b 100644
--- a/ghost/admin-x-settings/src/App.tsx
+++ b/ghost/admin-x-settings/src/App.tsx
@@ -1,9 +1,9 @@
import Button from './admin-x-ds/global/Button';
+import DataProvider from './components/providers/DataProvider';
import Heading from './admin-x-ds/global/Heading';
import NiceModal from '@ebay/nice-modal-react';
import Settings from './components/Settings';
import Sidebar from './components/Sidebar';
-import {SettingsProvider} from './components/SettingsProvider';
function App() {
return (
@@ -26,9 +26,9 @@ function App() {
-
+
-
+
diff --git a/ghost/admin-x-settings/src/components/Settings.tsx b/ghost/admin-x-settings/src/components/Settings.tsx
index e9513b1f3e..7b170841fc 100644
--- a/ghost/admin-x-settings/src/components/Settings.tsx
+++ b/ghost/admin-x-settings/src/components/Settings.tsx
@@ -3,7 +3,7 @@ import React, {useContext} from 'react';
import EmailSettings from './settings/email/EmailSettings';
import GeneralSettings from './settings/general/GeneralSettings';
import MembershipSettings from './settings/membership/MembershipSettings';
-import {SettingsContext} from './SettingsProvider';
+import {SettingsContext} from './providers/SettingsProvider';
const Settings: React.FC = () => {
const {settings} = useContext(SettingsContext) || {};
diff --git a/ghost/admin-x-settings/src/components/providers/DataProvider.tsx b/ghost/admin-x-settings/src/components/providers/DataProvider.tsx
new file mode 100644
index 0000000000..c92396046c
--- /dev/null
+++ b/ghost/admin-x-settings/src/components/providers/DataProvider.tsx
@@ -0,0 +1,19 @@
+import React from 'react';
+import {SettingsProvider} from './SettingsProvider';
+import {UsersProvider} from './UsersProvider';
+
+type DataProviderProps = {
+ children: React.ReactNode;
+};
+
+const DataProvider: React.FC = ({children}) => {
+ return (
+
+
+ {children}
+
+
+ );
+};
+
+export default DataProvider;
\ No newline at end of file
diff --git a/ghost/admin-x-settings/src/components/SettingsProvider.tsx b/ghost/admin-x-settings/src/components/providers/SettingsProvider.tsx
similarity index 94%
rename from ghost/admin-x-settings/src/components/SettingsProvider.tsx
rename to ghost/admin-x-settings/src/components/providers/SettingsProvider.tsx
index 662a80759d..0e125affd5 100644
--- a/ghost/admin-x-settings/src/components/SettingsProvider.tsx
+++ b/ghost/admin-x-settings/src/components/providers/SettingsProvider.tsx
@@ -1,6 +1,6 @@
import React, {createContext, useEffect, useState} from 'react';
-import {Setting} from '../types/api';
-import {getSettings, updateSettings} from '../utils/api';
+import {Setting} from '../../types/api';
+import {getSettings, updateSettings} from '../../utils/api';
// Define the Settings Context
interface SettingsContextProps {
diff --git a/ghost/admin-x-settings/src/components/providers/UsersProvider.tsx b/ghost/admin-x-settings/src/components/providers/UsersProvider.tsx
new file mode 100644
index 0000000000..3cf1ab2f7e
--- /dev/null
+++ b/ghost/admin-x-settings/src/components/providers/UsersProvider.tsx
@@ -0,0 +1,42 @@
+import React, {createContext, useEffect, useState} from 'react';
+import {User} from '../../types/api';
+import {getUsers} from '../../utils/api';
+
+interface UsersContextProps {
+ users: User[];
+}
+
+interface UsersProviderProps {
+ children?: React.ReactNode;
+}
+
+const UsersContext = createContext({
+ users: []
+});
+
+const UsersProvider: React.FC = ({children}) => {
+ const [users, setUsers] = useState ([]);
+
+ useEffect(() => {
+ const fetchUsers = async (): Promise => {
+ try {
+ // get list of staff users from the API
+ const data = await getUsers();
+ setUsers(data.users);
+ } catch (error) {
+ // Log error in API
+ }
+ };
+
+ fetchUsers();
+ }, []);
+
+ // Provide the settings and the saveSettings function to the children components
+ return (
+
+ {children}
+
+ );
+};
+
+export {UsersContext, UsersProvider};
diff --git a/ghost/admin-x-settings/src/components/settings/general/SocialAccounts.tsx b/ghost/admin-x-settings/src/components/settings/general/SocialAccounts.tsx
index 5e45c865d9..0affadacdc 100644
--- a/ghost/admin-x-settings/src/components/settings/general/SocialAccounts.tsx
+++ b/ghost/admin-x-settings/src/components/settings/general/SocialAccounts.tsx
@@ -2,7 +2,7 @@ import React, {useContext, 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 {SettingsContext} from '../../SettingsProvider';
+import {SettingsContext} from '../../providers/SettingsProvider';
import {TSettingGroupStates} from '../../../admin-x-ds/settings/SettingGroup';
import {getSettingValue} from '../../../utils/helpers';
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 7d32476aa4..2ebc643ce2 100644
--- a/ghost/admin-x-settings/src/components/settings/general/Users.tsx
+++ b/ghost/admin-x-settings/src/components/settings/general/Users.tsx
@@ -7,14 +7,61 @@ import React from 'react';
import SettingGroup from '../../../admin-x-ds/settings/SettingGroup';
import TabView from '../../../admin-x-ds/global/TabView';
import UserDetailModal from './modals/UserDetailModal';
+import useStaffUsers from '../../../hooks/useStaffUsers';
-const Users: React.FC = () => {
- const showInviteModal = () => {
- NiceModal.show(InviteUserModal);
+const Owner: React.FC = ({user}) => {
+ if (!user) {
+ return null;
+ }
+ return (
+
+ {user.name} — Owner
+ {user.email}
+
+ );
+};
+
+const UsersList: React.FC = ({users}) => {
+ const showDetailModal = () => {
+ NiceModal.show(UserDetailModal);
};
- const showDetailModal = () => {
- NiceModal.show(UserDetailModal);
+ if (!users || !users.length) {
+ return (
+
+ );
+ }
+
+ return (
+
+ {users.map((user: any) => {
+ return (
+ }
+ detail={user.email}
+ hideActions={true}
+ id={`list-item-${user.id}`}
+ title={user.name} />
+ );
+ })}
+
+ );
+};
+
+const Users: React.FC = () => {
+ const {
+ ownerUser,
+ adminUsers,
+ editorUsers,
+ authorUsers,
+ contributorUsers
+ } = useStaffUsers();
+
+ const showInviteModal = () => {
+ NiceModal.show(InviteUserModal);
};
const buttons = (
@@ -23,52 +70,35 @@ const Users: React.FC = () => {
}} />
);
- const owner = (
-
- Cristofer Vaccaro — Owner
- cristofer@example.com
-
- );
-
- const admins = (
-
- }
- detail='alena@press.com'
- hideActions={true}
- id='list-item-1'
- title='Alena Press' />
-
- );
-
- const editors = (
-
- }
- detail='lydia@siphron.com'
- hideActions={true}
- id='list-item-1'
- title='Lydia Siphron' />
- }
- detail='james@korsgaard.com'
- hideActions={true}
- id='list-item-1'
- title='James Korsgaard' />
-
- );
-
const tabs = [
- {id: 'users-admins', title: 'Administrators', contents: admins},
- {id: 'users-editors', title: 'Editors', contents: editors}
+ {
+ id: 'users-admins',
+ title: 'Administrators',
+ contents: ()
+ },
+ {
+ id: 'users-editors',
+ title: 'Editors',
+ contents: ()
+ },
+ {
+ id: 'users-authors',
+ title: 'Authors',
+ contents: ()
+ },
+ {
+ id: 'users-contributors',
+ title: 'Contributors',
+ contents: ()
+ }
];
return (
-
- {owner}
+
);
diff --git a/ghost/admin-x-settings/src/hooks/useSettingGroup.tsx b/ghost/admin-x-settings/src/hooks/useSettingGroup.tsx
index e48d23f184..5d25ca7479 100644
--- a/ghost/admin-x-settings/src/hooks/useSettingGroup.tsx
+++ b/ghost/admin-x-settings/src/hooks/useSettingGroup.tsx
@@ -1,6 +1,6 @@
import React, {useEffect} from 'react';
import {Setting, SettingValue} from '../types/api';
-import {SettingsContext} from '../components/SettingsProvider';
+import {SettingsContext} from '../components/providers/SettingsProvider';
import {TSettingGroupStates} from '../admin-x-ds/settings/SettingGroup';
import {useContext, useReducer, useRef, useState} from 'react';
diff --git a/ghost/admin-x-settings/src/hooks/useStaffUsers.tsx b/ghost/admin-x-settings/src/hooks/useStaffUsers.tsx
new file mode 100644
index 0000000000..6472e6a7b1
--- /dev/null
+++ b/ghost/admin-x-settings/src/hooks/useStaffUsers.tsx
@@ -0,0 +1,39 @@
+import {User} from '../types/api';
+import {UsersContext} from '../components/providers/UsersProvider';
+import {useContext} from 'react';
+
+export type UsersHook = {
+ users: User[];
+ ownerUser: User;
+ adminUsers: User[];
+ editorUsers: User[];
+ authorUsers: User[];
+ contributorUsers: User[];
+};
+
+function getUsersByRole(users: User[], role: string): User[] {
+ return users.filter((user) => {
+ return user.roles.find((userRole) => {
+ return userRole.name === role;
+ });
+ });
+}
+
+const useStaffUsers = (): UsersHook => {
+ const {users} = useContext(UsersContext);
+ const ownerUser = getUsersByRole(users, 'Owner')[0] || null;
+ const adminUsers = getUsersByRole(users, 'Administrator');
+ const editorUsers = getUsersByRole(users, 'Editor');
+ const authorUsers = getUsersByRole(users, 'Author');
+ const contributorUsers = getUsersByRole(users, 'Contributor');
+ return {
+ users,
+ ownerUser,
+ adminUsers,
+ editorUsers,
+ authorUsers,
+ contributorUsers
+ };
+};
+
+export default useStaffUsers;
diff --git a/ghost/admin-x-settings/src/types/api.ts b/ghost/admin-x-settings/src/types/api.ts
index eff2b6a963..89f6265b29 100644
--- a/ghost/admin-x-settings/src/types/api.ts
+++ b/ghost/admin-x-settings/src/types/api.ts
@@ -4,3 +4,43 @@ export type Setting = {
key: string;
value: SettingValue;
}
+
+export type User = {
+ id: string;
+ name: string;
+ slug: string;
+ email: string;
+ profile_image: string;
+ cover_image: string|null;
+ bio: string;
+ website: string;
+ location: string;
+ facebook: string;
+ twitter: string;
+ accessibility: string|null;
+ status: string;
+ meta_title: string|null;
+ meta_description: string|null;
+ tour: string|null;
+ last_seen: string|null;
+ created_at: string;
+ updated_at: string;
+ comment_notifications: boolean;
+ free_member_signup_notification: boolean;
+ paid_subscription_canceled_notification: boolean;
+ paid_subscription_started_notification: boolean;
+ mention_notifications: boolean;
+ milestone_notifications: boolean;
+ roles: UserRole[];
+ url: string;
+}
+
+export type UserRoleType = 'Owner' | 'Administrator' | 'Editor' | 'Author' | 'Contributor';
+
+export type UserRole = {
+ id: string;
+ name: UserRoleType;
+ description: string;
+ created_at: string;
+ updated_at: string;
+};
diff --git a/ghost/admin-x-settings/src/utils/api.ts b/ghost/admin-x-settings/src/utils/api.ts
index 20091f0d0d..6486b398d9 100644
--- a/ghost/admin-x-settings/src/utils/api.ts
+++ b/ghost/admin-x-settings/src/utils/api.ts
@@ -1,20 +1,40 @@
-import {Setting} from '../types/api';
+import {Setting, User} from '../types/api';
import {getGhostPaths} from './helpers';
-interface IQueryParams {
+type ApiQueryParams = {
+ limit: string;
+ include: string;
+ [key: string]: string;
+}
+
+type SettingApiQueryParams = {
group: string;
[key: string]: string;
}
-// Define the SettingsResponse type
-export interface ISettingsResponse {
- meta: any;
+type Meta = {
+ pagination: {
+ page: number;
+ limit: number;
+ pages: number;
+ total: number;
+ next: number;
+ prev: number;
+ }
+}
+
+export type SettingsResponseType = {
+ meta: Meta;
settings: Setting[];
}
+export type UsersResponseType = {
+ meta: Meta;
+ users: User[];
+}
export async function getSettings() {
const {apiRoot} = getGhostPaths();
- const queryParams: IQueryParams = {group: 'site,theme,private,members,portal,newsletter,email,amp,labs,slack,unsplash,views,firstpromoter,editor,comments,analytics,announcement,pintura'};
+ const queryParams: SettingApiQueryParams = {group: 'site,theme,private,members,portal,newsletter,email,amp,labs,slack,unsplash,views,firstpromoter,editor,comments,analytics,announcement,pintura'};
const queryString = Object.keys(queryParams).map((key) => {
return `${key}=${queryParams[key] || ''}`;
}).join('&');
@@ -28,7 +48,7 @@ export async function getSettings() {
mode: 'cors',
credentials: 'include'
});
- const data: ISettingsResponse = await response.json();
+ const data: SettingsResponseType = await response.json();
return data;
}
@@ -51,6 +71,26 @@ export async function updateSettings(newSettings: Setting[]) {
credentials: 'include'
});
- const data: ISettingsResponse = await response.json();
+ const data: SettingsResponseType = await response.json();
+ return data;
+}
+
+export async function getUsers() {
+ const {apiRoot} = getGhostPaths();
+ const queryParams: ApiQueryParams = {limit: 'all', include: 'roles'};
+ const queryString = Object.keys(queryParams).map((key) => {
+ return `${key}=${queryParams[key] || ''}`;
+ }).join('&');
+
+ const response = await fetch(`${apiRoot}/users/?${queryString}`, {
+ headers: {
+ 'app-pragma': 'no-cache',
+ 'x-ghost-version': '5.47'
+ },
+ method: 'GET',
+ mode: 'cors',
+ credentials: 'include'
+ });
+ const data: UsersResponseType = await response.json();
return data;
}