UserProfile: convert user organizations section to react (#18707)

* convert user organizations section to react

* reload page on org change
This commit is contained in:
Shavonn Brown 2019-09-05 06:47:20 -04:00 committed by Torkel Ödegaard
parent 849d4881cd
commit e80e3608ad
6 changed files with 126 additions and 56 deletions

View File

@ -1,19 +1,24 @@
import React, { PureComponent } from 'react';
import { getBackendSrv } from '@grafana/runtime';
import { User, Team } from 'app/types';
import { config } from 'app/core/config';
export interface UserAPI {
changePassword: (changePassword: ChangePasswordFields) => void;
updateUserProfile: (profile: ProfileUpdateFields) => void;
loadUser: () => void;
loadTeams: () => void;
loadOrgs: () => void;
setUserOrg: (org: UserOrg) => void;
}
interface LoadingStates {
changePassword: boolean;
loadUser: boolean;
loadTeams: boolean;
loadOrgs: boolean;
updateUserProfile: boolean;
updateUserOrg: boolean;
}
export interface ChangePasswordFields {
@ -28,25 +33,35 @@ export interface ProfileUpdateFields {
login: string;
}
export interface UserOrg {
orgId: number;
name: string;
role: string;
}
export interface Props {
userId?: number; // passed, will load user on mount
children: (api: UserAPI, states: LoadingStates, teams: Team[], user?: User) => JSX.Element;
children: (api: UserAPI, states: LoadingStates, teams: Team[], orgs: UserOrg[], user?: User) => JSX.Element;
}
export interface State {
user?: User;
teams: Team[];
orgs: UserOrg[];
loadingStates: LoadingStates;
}
export class UserProvider extends PureComponent<Props, State> {
state: State = {
teams: [] as Team[],
orgs: [] as UserOrg[],
loadingStates: {
changePassword: false,
loadUser: true,
loadTeams: false,
loadOrgs: false,
updateUserProfile: false,
updateUserOrg: false,
},
};
@ -78,6 +93,28 @@ export class UserProvider extends PureComponent<Props, State> {
this.setState({ teams, loadingStates: { ...this.state.loadingStates, loadTeams: false } });
};
loadOrgs = async () => {
this.setState({
loadingStates: { ...this.state.loadingStates, loadOrgs: true },
});
const orgs = await getBackendSrv().get('/api/user/orgs');
this.setState({ orgs, loadingStates: { ...this.state.loadingStates, loadOrgs: false } });
};
setUserOrg = async (org: UserOrg) => {
this.setState({
loadingStates: { ...this.state.loadingStates, updateUserOrg: true },
});
await getBackendSrv()
.post('/api/user/using/' + org.orgId, {})
.then(() => {
window.location.href = config.appSubUrl + '/profile';
})
.finally(() => {
this.setState({ loadingStates: { ...this.state.loadingStates, updateUserOrg: false } });
});
};
updateUserProfile = async (payload: ProfileUpdateFields) => {
this.setState({ loadingStates: { ...this.state.loadingStates, updateUserProfile: true } });
await getBackendSrv()
@ -93,16 +130,18 @@ export class UserProvider extends PureComponent<Props, State> {
render() {
const { children } = this.props;
const { loadingStates, teams, user } = this.state;
const { loadingStates, teams, orgs, user } = this.state;
const api = {
changePassword: this.changePassword,
loadUser: this.loadUser,
loadTeams: this.loadTeams,
loadOrgs: this.loadOrgs,
updateUserProfile: this.updateUserProfile,
setUserOrg: this.setUserOrg,
};
return <>{children(api, loadingStates, teams, user)}</>;
return <>{children(api, loadingStates, teams, orgs, user)}</>;
}
}

View File

@ -1,23 +1,15 @@
import config from 'app/core/config';
import { coreModule, NavModelSrv } from 'app/core/core';
import { dateTime } from '@grafana/data';
import { UserSession } from 'app/types';
import { BackendSrv } from 'app/core/services/backend_srv';
export class ProfileCtrl {
user: any;
oldTheme: any;
orgs: any = [];
sessions: object[] = [];
userForm: any;
showOrgsList = false;
readonlyLoginFields = config.disableLoginForm;
navModel: any;
/** @ngInject */
constructor(private backendSrv: BackendSrv, navModelSrv: NavModelSrv) {
this.getUserSessions();
this.getUserOrgs();
this.navModel = navModelSrv.getNav('profile', 'profile-settings', 0);
}
@ -66,19 +58,6 @@ export class ProfileCtrl {
});
});
}
getUserOrgs() {
this.backendSrv.get('/api/user/orgs').then((orgs: any) => {
this.orgs = orgs;
this.showOrgsList = orgs.length > 1;
});
}
setUsingOrg(org: any) {
this.backendSrv.post('/api/user/using/' + org.orgId).then(() => {
window.location.href = config.appSubUrl + '/profile';
});
}
}
coreModule.controller('ProfileCtrl', ProfileCtrl);

View File

@ -3,12 +3,13 @@ import { UserProvider } from 'app/core/utils/UserProvider';
import { UserProfileEditForm } from './UserProfileEditForm';
import { SharedPreferences } from 'app/core/components/SharedPreferences/SharedPreferences';
import { UserTeams } from './UserTeams';
import { UserOrganizations } from './UserOrganizations';
import { config } from '@grafana/runtime';
import { LoadingPlaceholder } from '@grafana/ui';
export const ReactProfileWrapper = () => (
<UserProvider userId={config.bootData.user.id}>
{(api, states, teams, user) => {
{(api, states, teams, orgs, user) => {
return (
<>
{states.loadUser ? (
@ -22,6 +23,13 @@ export const ReactProfileWrapper = () => (
)}
<SharedPreferences resourceUri="user" />
<UserTeams isLoading={states.loadTeams} loadTeams={api.loadTeams} teams={teams} />
<UserOrganizations
isLoading={states.loadOrgs}
setUserOrg={api.setUserOrg}
loadOrgs={api.loadOrgs}
orgs={orgs}
user={user}
/>
</>
);
}}

View File

@ -0,0 +1,74 @@
import React, { PureComponent } from 'react';
import { User } from 'app/types';
import { UserOrg } from 'app/core/utils/UserProvider';
import { LoadingPlaceholder, Button } from '@grafana/ui';
export interface Props {
user: User;
orgs: UserOrg[];
isLoading: boolean;
loadOrgs: () => void;
setUserOrg: (org: UserOrg) => void;
}
export class UserOrganizations extends PureComponent<Props> {
componentDidMount() {
this.props.loadOrgs();
}
render() {
const { isLoading, orgs, user } = this.props;
if (isLoading) {
return <LoadingPlaceholder text="Loading organizations..." />;
}
return (
<>
{orgs.length > 0 && (
<>
<h3 className="page-sub-heading">Organizations</h3>
<div className="gf-form-group">
<table className="filter-table form-inline">
<thead>
<tr>
<th>Name</th>
<th>Role</th>
<th />
</tr>
</thead>
<tbody>
{orgs.map((org: UserOrg, index) => {
return (
<tr key={index}>
<td>{org.name}</td>
<td>{org.role}</td>
<td className="text-right">
{org.orgId === user.orgId ? (
<span className="btn btn-primary btn-small">Current</span>
) : (
<Button
variant="inverse"
size="sm"
onClick={() => {
this.props.setUserOrg(org);
}}
>
Select
</Button>
)}
</td>
</tr>
);
})}
</tbody>
</table>
</div>
</>
)}
</>
);
}
}
export default UserOrganizations;

View File

@ -3,37 +3,6 @@
<div class="page-container page-body">
<react-profile-wrapper></react-profile-wrapper>
<h3 class="page-heading" ng-show="ctrl.showOrgsList">Organizations</h3>
<div class="gf-form-group" ng-show="ctrl.showOrgsList">
<table class="filter-table form-inline">
<thead>
<tr>
<th>Name</th>
<th>Role</th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="org in ctrl.orgs">
<td>{{ org.name }}</td>
<td>{{ org.role }}</td>
<td class="text-right">
<span class="btn btn-primary btn-small" ng-show="org.orgId === contextSrv.user.orgId">
Current
</span>
<a
ng-click="ctrl.setUsingOrg(org)"
class="btn btn-inverse btn-small"
ng-show="org.orgId !== contextSrv.user.orgId"
>
Select
</a>
</td>
</tr>
</tbody>
</table>
</div>
<h3 class="page-heading">Sessions</h3>
<div class="gf-form-group">
<table class="filter-table form-inline">

View File

@ -18,6 +18,7 @@ export interface User {
login: string;
email: string;
name: string;
orgId?: number;
}
export interface Invitee {