Migration: Create org (#22542)

* Add NewOrgPage

* Remove filrs

* Remove import

* Remove comments

* Get navModel from Redux

* Add DTO

* Get nav model from state

* Change error display

* Add async await and check if org is available

* Fix null strict null error

* Update public/app/features/org/NewOrgPage.tsx

Co-Authored-By: Dominik Prokop <dominik.prokop@grafana.com>

Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
This commit is contained in:
Tobias Skarhed 2020-03-19 14:22:53 +01:00 committed by GitHub
parent f2742d4a28
commit a2b3da2792
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 101 additions and 53 deletions

View File

@ -16,9 +16,21 @@ export interface NavModelItem {
showOrgSwitcher?: boolean;
}
/**
* Interface used to describe different kinds of page titles and page navigation. Navmodels are usually generated in the backend and stored in Redux.
*/
export interface NavModel {
/**
* Main page. that wraps the navigation. Generate the `children` property generate tabs when used with the Page component.
*/
main: NavModelItem;
/**
* This is the current active tab/navigation.
*/
node: NavModelItem;
/**
* Describes breadcrumbs that are used in places such as data source settings., folder page and plugins page.
*/
breadcrumbs?: NavModelItem[];
}

View File

@ -1,26 +0,0 @@
import angular from 'angular';
import config from 'app/core/config';
import { getBackendSrv } from '@grafana/runtime';
import { NavModelSrv } from 'app/core/core';
export class NewOrgCtrl {
/** @ngInject */
constructor($scope: any, $http: any, navModelSrv: NavModelSrv) {
$scope.navModel = navModelSrv.getNav('admin', 'global-orgs', 0);
$scope.newOrg = { name: '' };
$scope.createOrg = () => {
getBackendSrv()
.post('/api/orgs/', $scope.newOrg)
.then((result: any) => {
getBackendSrv()
.post('/api/user/using/' + result.orgId)
.then(() => {
window.location.href = config.appSubUrl + '/org';
});
});
};
}
}
angular.module('grafana.controllers').controller('NewOrgCtrl', NewOrgCtrl);

View File

@ -0,0 +1,85 @@
import React, { FC } from 'react';
import { getBackendSrv } from '@grafana/runtime';
import Page from 'app/core/components/Page/Page';
import { Forms } from '@grafana/ui';
import { getConfig } from 'app/core/config';
import { StoreState } from 'app/types';
import { hot } from 'react-hot-loader';
import { connect } from 'react-redux';
import { NavModel } from '@grafana/data';
import { getNavModel } from '../../core/selectors/navModel';
const createOrg = async (newOrg: { name: string }) => {
const result = await getBackendSrv().post('/api/orgs/', newOrg);
await getBackendSrv().post('/api/user/using/' + result.orgId);
window.location.href = getConfig().appSubUrl + '/org';
};
const validateOrg = async (orgName: string) => {
try {
await getBackendSrv().get(`api/orgs/name/${encodeURI(orgName)}`);
} catch (error) {
if (error.status === 404) {
error.isHandled = true;
return true;
}
return 'Something went wrong';
}
return 'Organization already exists';
};
interface PropsWithState {
navModel: NavModel;
}
interface CreateOrgFormDTO {
name: string;
}
export const NewOrgPage: FC<PropsWithState> = ({ navModel }) => {
return (
<Page navModel={navModel}>
<Page.Contents>
<h3 className="page-sub-heading">New Organization</h3>
<p className="playlist-description">
Each organization contains their own dashboards, data sources and configuration, and cannot be shared between
orgs. While users may belong to more than one, multiple organization are most frequently used in multi-tenant
deployments.{' '}
</p>
<Forms.Form<CreateOrgFormDTO> onSubmit={createOrg}>
{({ register, errors }) => {
return (
<>
<Forms.Field
label="Organization name"
invalid={!!errors.name}
error={errors.name && errors.name.message}
>
<Forms.Input
size="md"
placeholder="Org. name"
name="name"
ref={register({
required: 'Organization name is required',
validate: async orgName => await validateOrg(orgName),
})}
/>
</Forms.Field>
<Forms.Button type="submit">Create</Forms.Button>
</>
);
}}
</Forms.Form>
</Page.Contents>
</Page>
);
};
const mapStateToProps = (state: StoreState) => {
return { navModel: getNavModel(state.navIndex, 'global-orgs') };
};
export default hot(module)(connect(mapStateToProps)(NewOrgPage));

View File

@ -1,2 +1 @@
import './SelectOrgCtrl';
import './NewOrgCtrl';

View File

@ -1,24 +0,0 @@
<page-header model="navModel"></page-header>
<div class="page-container page-body" ng-form="playlistEditForm">
<h3 class="page-sub-heading">
New Organization
</h3>
<p class="playlist-description">Each organization contains their own dashboards, data sources and configuration, and cannot be shared between orgs. While users may belong to more than one, multiple organization are most frequently used in multi-tenant deployments. </p>
<form>
<div class="gf-form-group">
<div class="gf-form">
<span class="gf-form-label width-10">Org. name</span>
<input type="text" ng-model="newOrg.name" required class="gf-form-input max-width-21" placeholder="organization name">
</div>
<br>
<div class="gf-form-buttons-row">
<button type="submit" class="btn btn-primary" ng-click="createOrg()">Create</button>
</div>
</div>
</form>
</div>
<footer />

View File

@ -219,8 +219,10 @@ export function setupAngularRoutes($routeProvider: route.IRouteProvider, $locati
},
})
.when('/org/new', {
templateUrl: 'public/app/features/org/partials/newOrg.html',
controller: 'NewOrgCtrl',
template: '<react-container />',
resolve: {
component: () => SafeDynamicImport(import(/* webpackChunkName: "NewOrgPage" */ 'app/features/org/NewOrgPage')),
},
})
.when('/org/users', {
template: '<react-container />',