From a2b3da279216a025186f86b6e99c518d03cf9ca1 Mon Sep 17 00:00:00 2001 From: Tobias Skarhed <1438972+tskarhed@users.noreply.github.com> Date: Thu, 19 Mar 2020 14:22:53 +0100 Subject: [PATCH] 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 Co-authored-by: Dominik Prokop --- packages/grafana-data/src/types/navModel.ts | 12 +++ public/app/features/org/NewOrgCtrl.ts | 26 ------ public/app/features/org/NewOrgPage.tsx | 85 ++++++++++++++++++++ public/app/features/org/all.ts | 1 - public/app/features/org/partials/newOrg.html | 24 ------ public/app/routes/routes.ts | 6 +- 6 files changed, 101 insertions(+), 53 deletions(-) delete mode 100644 public/app/features/org/NewOrgCtrl.ts create mode 100644 public/app/features/org/NewOrgPage.tsx delete mode 100644 public/app/features/org/partials/newOrg.html diff --git a/packages/grafana-data/src/types/navModel.ts b/packages/grafana-data/src/types/navModel.ts index 7d23f222e14..ff6ca48f501 100644 --- a/packages/grafana-data/src/types/navModel.ts +++ b/packages/grafana-data/src/types/navModel.ts @@ -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[]; } diff --git a/public/app/features/org/NewOrgCtrl.ts b/public/app/features/org/NewOrgCtrl.ts deleted file mode 100644 index 0694873c819..00000000000 --- a/public/app/features/org/NewOrgCtrl.ts +++ /dev/null @@ -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); diff --git a/public/app/features/org/NewOrgPage.tsx b/public/app/features/org/NewOrgPage.tsx new file mode 100644 index 00000000000..9059ad10b16 --- /dev/null +++ b/public/app/features/org/NewOrgPage.tsx @@ -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 = ({ navModel }) => { + return ( + + +

New Organization

+ +

+ 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.{' '} +

+ + onSubmit={createOrg}> + {({ register, errors }) => { + return ( + <> + + await validateOrg(orgName), + })} + /> + + Create + + ); + }} + +
+
+ ); +}; + +const mapStateToProps = (state: StoreState) => { + return { navModel: getNavModel(state.navIndex, 'global-orgs') }; +}; + +export default hot(module)(connect(mapStateToProps)(NewOrgPage)); diff --git a/public/app/features/org/all.ts b/public/app/features/org/all.ts index 92de5eba2aa..ecdaf0b7d55 100644 --- a/public/app/features/org/all.ts +++ b/public/app/features/org/all.ts @@ -1,2 +1 @@ import './SelectOrgCtrl'; -import './NewOrgCtrl'; diff --git a/public/app/features/org/partials/newOrg.html b/public/app/features/org/partials/newOrg.html deleted file mode 100644 index ce0645b6297..00000000000 --- a/public/app/features/org/partials/newOrg.html +++ /dev/null @@ -1,24 +0,0 @@ - - -
-

- New Organization -

- -

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.

- -
-
-
- Org. name - -
-
-
- -
-
-
-
- -