mirror of
https://github.com/grafana/grafana.git
synced 2025-01-04 13:17:16 -06:00
Migration: User invite (#22263)
* Add new form and functionality * Add new files * Connect to Redux and add navigation stuff * Add required login/name * Remove import * Fix feedback * Replace direct button spacing with HorizontalGroup Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
This commit is contained in:
parent
9cf84c51f5
commit
d2689b6d6c
@ -2,13 +2,17 @@ import { getFormStyles } from './getFormStyles';
|
||||
import { Label } from './Label';
|
||||
import { Input } from './Input/Input';
|
||||
import { ButtonSelect } from './Select/ButtonSelect';
|
||||
import { RadioButtonGroup } from './RadioButtonGroup/RadioButtonGroup';
|
||||
import { AsyncSelect, Select } from './Select/Select';
|
||||
import { Form } from './Form';
|
||||
import { Field } from './Field';
|
||||
import { Button, LinkButton } from './Button';
|
||||
import { Switch } from './Switch';
|
||||
import { Controller as InputControl } from 'react-hook-form';
|
||||
|
||||
const Forms = {
|
||||
RadioButtonGroup,
|
||||
Switch,
|
||||
getFormStyles,
|
||||
Label,
|
||||
Input,
|
||||
|
@ -1,39 +0,0 @@
|
||||
import coreModule from 'app/core/core_module';
|
||||
import { getBackendSrv } from '@grafana/runtime';
|
||||
import { NavModelSrv } from 'app/core/core';
|
||||
import { ILocationService, IScope } from 'angular';
|
||||
import { promiseToDigest } from 'app/core/utils/promiseToDigest';
|
||||
|
||||
export class UserInviteCtrl {
|
||||
navModel: any;
|
||||
invite: any;
|
||||
inviteForm: any;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private $scope: IScope, navModelSrv: NavModelSrv, private $location: ILocationService) {
|
||||
this.navModel = navModelSrv.getNav('cfg', 'users', 0);
|
||||
|
||||
this.invite = {
|
||||
name: '',
|
||||
email: '',
|
||||
role: 'Editor',
|
||||
sendEmail: true,
|
||||
};
|
||||
}
|
||||
|
||||
sendInvite() {
|
||||
if (!this.inviteForm.$valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
promiseToDigest(this.$scope)(
|
||||
getBackendSrv()
|
||||
.post('/api/org/invites', this.invite)
|
||||
.then(() => {
|
||||
this.$location.path('org/users/');
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
coreModule.controller('UserInviteCtrl', UserInviteCtrl);
|
90
public/app/features/org/UserInviteForm.tsx
Normal file
90
public/app/features/org/UserInviteForm.tsx
Normal file
@ -0,0 +1,90 @@
|
||||
import React, { FC } from 'react';
|
||||
import { Forms, HorizontalGroup } from '@grafana/ui';
|
||||
import { getConfig } from 'app/core/config';
|
||||
import { OrgRole } from 'app/types';
|
||||
import { getBackendSrv } from '@grafana/runtime';
|
||||
import { updateLocation } from 'app/core/actions';
|
||||
import { connect } from 'react-redux';
|
||||
import { hot } from 'react-hot-loader';
|
||||
import { appEvents } from 'app/core/core';
|
||||
import { AppEvents } from '@grafana/data';
|
||||
import { assureBaseUrl } from 'app/core/utils/location_util';
|
||||
|
||||
const roles = [
|
||||
{ label: 'Viewer', value: OrgRole.Viewer },
|
||||
{ label: 'Editor', value: OrgRole.Editor },
|
||||
{ label: 'Admin', value: OrgRole.Admin },
|
||||
];
|
||||
|
||||
interface FormModel {
|
||||
role: OrgRole;
|
||||
name: string;
|
||||
loginOrEmail?: string;
|
||||
sendEmail: boolean;
|
||||
email: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
updateLocation: typeof updateLocation;
|
||||
}
|
||||
|
||||
export const UserInviteForm: FC<Props> = ({ updateLocation }) => {
|
||||
const onSubmit = async (formData: FormModel) => {
|
||||
try {
|
||||
await getBackendSrv().post('/api/org/invites', formData);
|
||||
} catch (err) {
|
||||
appEvents.emit(AppEvents.alertError, ['Failed to send invite', err.message]);
|
||||
}
|
||||
updateLocation({ path: 'org/users/' });
|
||||
};
|
||||
const defaultValues: FormModel = {
|
||||
name: '',
|
||||
email: '',
|
||||
role: OrgRole.Editor,
|
||||
sendEmail: true,
|
||||
};
|
||||
|
||||
return (
|
||||
<Forms.Form defaultValues={defaultValues} onSubmit={onSubmit}>
|
||||
{({ register, control, errors }) => {
|
||||
return (
|
||||
<>
|
||||
<Forms.Field
|
||||
invalid={!!errors.loginOrEmail}
|
||||
error={!!errors.loginOrEmail && 'Email or Username is required'}
|
||||
label="Email or Username"
|
||||
>
|
||||
<Forms.Input
|
||||
size="md"
|
||||
name="loginOrEmail"
|
||||
placeholder="email@example.com"
|
||||
ref={register({ required: true })}
|
||||
/>
|
||||
</Forms.Field>
|
||||
<Forms.Field invalid={!!errors.name} label="Name">
|
||||
<Forms.Input size="md" name="name" placeholder="(optional)" ref={register} />
|
||||
</Forms.Field>
|
||||
<Forms.Field invalid={!!errors.role} label="Role">
|
||||
<Forms.InputControl as={Forms.RadioButtonGroup} control={control} options={roles} name="role" />
|
||||
</Forms.Field>
|
||||
<Forms.Field invalid={!!errors.sendEmail} label="Send invite email">
|
||||
<Forms.Switch name="sendEmail" ref={register} />
|
||||
</Forms.Field>
|
||||
<HorizontalGroup>
|
||||
<Forms.Button type="submit">Submit</Forms.Button>
|
||||
<Forms.LinkButton href={assureBaseUrl(getConfig().appSubUrl + '/org/users')} variant="secondary">
|
||||
Back
|
||||
</Forms.LinkButton>
|
||||
</HorizontalGroup>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</Forms.Form>
|
||||
);
|
||||
};
|
||||
|
||||
const mapDispatchToProps = {
|
||||
updateLocation,
|
||||
};
|
||||
|
||||
export default hot(module)(connect(null, mapDispatchToProps)(UserInviteForm));
|
33
public/app/features/org/UserInvitePage.tsx
Normal file
33
public/app/features/org/UserInvitePage.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import React, { FC } from 'react';
|
||||
import { hot } from 'react-hot-loader';
|
||||
import { connect } from 'react-redux';
|
||||
import UserInviteForm from './UserInviteForm';
|
||||
import { contextSrv, NavModel } from 'app/core/core';
|
||||
import { getNavModel } from 'app/core/selectors/navModel';
|
||||
import { StoreState } from 'app/types/store';
|
||||
import Page from 'app/core/components/Page/Page';
|
||||
|
||||
interface Props {
|
||||
navModel: NavModel;
|
||||
}
|
||||
|
||||
export const UserInvitePage: FC<Props> = ({ navModel }) => {
|
||||
return (
|
||||
<Page navModel={navModel}>
|
||||
<Page.Contents>
|
||||
<h3 className="page-sub-heading">Invite User</h3>
|
||||
<div className="p-b-2">
|
||||
Send invite or add existing Grafana user to the organization
|
||||
<span className="highlight-word"> {contextSrv.user.orgName}</span>
|
||||
</div>
|
||||
<UserInviteForm />
|
||||
</Page.Contents>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
const mapStateToProps = (state: StoreState) => ({
|
||||
navModel: getNavModel(state.navIndex, 'users'),
|
||||
});
|
||||
|
||||
export default hot(module)(connect(mapStateToProps)(UserInvitePage));
|
@ -1,3 +1,2 @@
|
||||
import './SelectOrgCtrl';
|
||||
import './NewOrgCtrl';
|
||||
import './UserInviteCtrl';
|
||||
|
@ -1,35 +0,0 @@
|
||||
<page-header model="ctrl.navModel"></page-header>
|
||||
|
||||
<div class="page-container page-body" ng-cloak>
|
||||
|
||||
<h3 class="page-sub-heading">Invite User</h3>
|
||||
|
||||
<div class="p-b-2">
|
||||
Send invite or add existing Grafana user to the organization
|
||||
<span class="highlight-word">{{contextSrv.user.orgName}}</span>
|
||||
</div>
|
||||
|
||||
<form name="ctrl.inviteForm">
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form max-width-30">
|
||||
<span class="gf-form-label width-10">Email or Username</span>
|
||||
<input type="text" ng-model="ctrl.invite.loginOrEmail" required class="gf-form-input" placeholder="email@test.com">
|
||||
</div>
|
||||
<div class="gf-form max-width-30">
|
||||
<span class="gf-form-label width-10">Name</span>
|
||||
<input type="text" ng-model="ctrl.invite.name" class="gf-form-input" placeholder="name (optional)">
|
||||
</div>
|
||||
<div class="gf-form max-width-30">
|
||||
<span class="gf-form-label width-10">Role</span>
|
||||
<select ng-model="ctrl.invite.role" class="gf-form-input" ng-options="f for f in ['Viewer', 'Editor', 'Admin']">
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<gf-form-switch class="gf-form" label="Send invite email" checked="ctrl.invite.sendEmail" label-class="width-10"></gf-form-switch>
|
||||
|
||||
<div class="gf-form-button-row">
|
||||
<button type="submit" class="btn btn-primary" ng-click="ctrl.sendInvite();">Invite</button>
|
||||
<a class="btn btn-inverse" href="org/users">Back</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
@ -227,9 +227,11 @@ export function setupAngularRoutes($routeProvider: route.IRouteProvider, $locati
|
||||
},
|
||||
})
|
||||
.when('/org/users/invite', {
|
||||
templateUrl: 'public/app/features/org/partials/invite.html',
|
||||
controller: 'UserInviteCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
template: '<react-container/>',
|
||||
resolve: {
|
||||
component: () =>
|
||||
SafeDynamicImport(import(/* webpackChunkName: "UserInvitePage" */ 'app/features/org/UserInvitePage')),
|
||||
},
|
||||
})
|
||||
.when('/org/apikeys', {
|
||||
template: '<react-container />',
|
||||
|
Loading…
Reference in New Issue
Block a user