diff --git a/packages/grafana-ui/src/components/Forms/index.ts b/packages/grafana-ui/src/components/Forms/index.ts index 0cf719b7cfe..92fe11cdc2c 100644 --- a/packages/grafana-ui/src/components/Forms/index.ts +++ b/packages/grafana-ui/src/components/Forms/index.ts @@ -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, diff --git a/public/app/features/org/UserInviteCtrl.ts b/public/app/features/org/UserInviteCtrl.ts deleted file mode 100644 index 1cd4bca496c..00000000000 --- a/public/app/features/org/UserInviteCtrl.ts +++ /dev/null @@ -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); diff --git a/public/app/features/org/UserInviteForm.tsx b/public/app/features/org/UserInviteForm.tsx new file mode 100644 index 00000000000..6f18426c4ce --- /dev/null +++ b/public/app/features/org/UserInviteForm.tsx @@ -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 = ({ 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 ( + + {({ register, control, errors }) => { + return ( + <> + + + + + + + + + + + + + + Submit + + Back + + + + ); + }} + + ); +}; + +const mapDispatchToProps = { + updateLocation, +}; + +export default hot(module)(connect(null, mapDispatchToProps)(UserInviteForm)); diff --git a/public/app/features/org/UserInvitePage.tsx b/public/app/features/org/UserInvitePage.tsx new file mode 100644 index 00000000000..535dbeca7ba --- /dev/null +++ b/public/app/features/org/UserInvitePage.tsx @@ -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 = ({ navModel }) => { + return ( + + +

Invite User

+
+ Send invite or add existing Grafana user to the organization + {contextSrv.user.orgName} +
+ +
+
+ ); +}; + +const mapStateToProps = (state: StoreState) => ({ + navModel: getNavModel(state.navIndex, 'users'), +}); + +export default hot(module)(connect(mapStateToProps)(UserInvitePage)); diff --git a/public/app/features/org/all.ts b/public/app/features/org/all.ts index c905853cf49..92de5eba2aa 100644 --- a/public/app/features/org/all.ts +++ b/public/app/features/org/all.ts @@ -1,3 +1,2 @@ import './SelectOrgCtrl'; import './NewOrgCtrl'; -import './UserInviteCtrl'; diff --git a/public/app/features/org/partials/invite.html b/public/app/features/org/partials/invite.html deleted file mode 100644 index 2d9b1d49702..00000000000 --- a/public/app/features/org/partials/invite.html +++ /dev/null @@ -1,35 +0,0 @@ - - -
- -

Invite User

- -
- Send invite or add existing Grafana user to the organization - {{contextSrv.user.orgName}} -
- -
-
-
- Email or Username - -
-
- Name - -
-
- Role - -
- - - -
- - Back -
- -
diff --git a/public/app/routes/routes.ts b/public/app/routes/routes.ts index 5864a8be399..6e6b7727b72 100644 --- a/public/app/routes/routes.ts +++ b/public/app/routes/routes.ts @@ -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: '', + resolve: { + component: () => + SafeDynamicImport(import(/* webpackChunkName: "UserInvitePage" */ 'app/features/org/UserInvitePage')), + }, }) .when('/org/apikeys', { template: '',