mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
feat(import): things are starting to work
This commit is contained in:
parent
0d3e06e68a
commit
ca8df67947
9
.flooignore
Normal file
9
.flooignore
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#*
|
||||||
|
*.o
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
*~
|
||||||
|
extern/
|
||||||
|
node_modules/
|
||||||
|
tmp
|
||||||
|
vendor/
|
76
public/app/core/components/dash_importer/dash_importer.html
Normal file
76
public/app/core/components/dash_importer/dash_importer.html
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<div class="modal-body">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h2 class="modal-header-title">
|
||||||
|
<i class="fa fa-upload"></i>
|
||||||
|
<span class="p-l-1">Import Dashboard</span>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<a class="modal-header-close" ng-click="dismiss();">
|
||||||
|
<i class="fa fa-remove"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-content" ng-cloak>
|
||||||
|
<div ng-if="model.step === 0">
|
||||||
|
<form class="gf-form-group">
|
||||||
|
<dash-upload on-upload="model.onUpload(dash)"></dash-upload>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<h5 class="section-heading">Or paste JSON:</h5>
|
||||||
|
|
||||||
|
<div class="gf-form-group">
|
||||||
|
<div class="gf-form">
|
||||||
|
<textarea rows="7" data-share-panel-url="" class="gf-form-input" ng-model="model.jsonText"></textarea>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-secondary" ng-click="model.loadJsonText()">
|
||||||
|
<i class="fa fa-paste"></i>
|
||||||
|
Load
|
||||||
|
</button>
|
||||||
|
<span ng-if="model.parseError" class="text-error p-l-1">
|
||||||
|
<i class="fa fa-warning"></i>
|
||||||
|
{{model.parseError}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ng-if="model.step === 2">
|
||||||
|
<div class="gf-form-group">
|
||||||
|
<h3 class="section-heading p-b-1" ng-if="model.nameExists">
|
||||||
|
<i class="fa fa-warning"></i> Dashboard with same title already exists
|
||||||
|
</h3>
|
||||||
|
<h3 class="section-heading p-b-1" ng-if="!model.nameExists">
|
||||||
|
<i class="fa fa-check"></i> Dashboard title available
|
||||||
|
</h3>
|
||||||
|
<div class="gf-form-inline">
|
||||||
|
<div class="gf-form gf-form--grow">
|
||||||
|
<label class="gf-form-label">New title</label>
|
||||||
|
<input type="text" class="gf-form-input" ng-model="model.dash.title" give-focus="true" ng-change="model.titleChanged()" ng-class="{'validation-error': model.nameExists}">
|
||||||
|
<button type="button" class="btn btn-success gf-form-btn width-10" ng-click="model.saveDashboard()">
|
||||||
|
<i class="fa fa-save"></i>
|
||||||
|
<span ng-show="model.nameExists">Overwrite & Open</span>
|
||||||
|
<span ng-show="!model.nameExists">Save & Open</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <table class="filter-table"> -->
|
||||||
|
<!-- <tbody> -->
|
||||||
|
<!-- <tr ng-repeat="step in model.steps"> -->
|
||||||
|
<!-- <td>{{step.name}}</td> -->
|
||||||
|
<!-- <td>{{step.status}}</td> -->
|
||||||
|
<!-- <td width="1%"> -->
|
||||||
|
<!-- <i class="fa fa-check" style="color: #39A039"></i> -->
|
||||||
|
<!-- </td> -->
|
||||||
|
<!-- </tr> -->
|
||||||
|
<!-- </tbody> -->
|
||||||
|
<!-- </table> -->
|
||||||
|
|
||||||
|
<div class="gf-form-button-row text-right">
|
||||||
|
<a class="btn-text" ng-click="dismiss();">Cancel</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
77
public/app/core/components/dash_importer/dash_importer.ts
Normal file
77
public/app/core/components/dash_importer/dash_importer.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
///<reference path="../../../headers/common.d.ts" />
|
||||||
|
|
||||||
|
import kbn from 'app/core/utils/kbn';
|
||||||
|
import coreModule from 'app/core/core_module';
|
||||||
|
|
||||||
|
import appEvents from 'app/core/app_events';
|
||||||
|
import {WizardFlow} from 'app/core/core';
|
||||||
|
|
||||||
|
var wnd: any = window;
|
||||||
|
|
||||||
|
export class DashImporter {
|
||||||
|
step: number;
|
||||||
|
jsonText: string;
|
||||||
|
parseError: string;
|
||||||
|
nameExists: boolean;
|
||||||
|
dash: any;
|
||||||
|
dismiss: any;
|
||||||
|
|
||||||
|
constructor(private backendSrv, private $location) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onUpload(dash) {
|
||||||
|
this.dash = dash;
|
||||||
|
this.dash.id = null;
|
||||||
|
|
||||||
|
this.backendSrv.saveDashboard(this.dash, {overwrite: false}).then(res => {
|
||||||
|
|
||||||
|
}).catch(err => {
|
||||||
|
if (err.data.status === 'name-exists') {
|
||||||
|
err.isHandled = true;
|
||||||
|
this.step = 2;
|
||||||
|
this.nameExists = true;
|
||||||
|
}
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
titleChanged() {
|
||||||
|
this.backendSrv.search({query: this.dash.title}).then(res => {
|
||||||
|
this.nameExists = false;
|
||||||
|
for (let hit of res) {
|
||||||
|
if (this.dash.title === hit.title) {
|
||||||
|
this.nameExists = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
saveDashboard() {
|
||||||
|
return this.backendSrv.saveDashboard(this.dash, {overwrite: true}).then(res => {
|
||||||
|
this.$location.url('dashboard/db/' + res.slug);
|
||||||
|
this.dismiss();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
loadJsonText() {
|
||||||
|
try {
|
||||||
|
this.parseError = '';
|
||||||
|
var dash = JSON.parse(this.jsonText);
|
||||||
|
this.onUpload(dash);
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
this.parseError = err.message;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
run() {
|
||||||
|
this.step = 0;
|
||||||
|
|
||||||
|
appEvents.emit('show-modal', {
|
||||||
|
src: 'public/app/core/components/dash_importer/dash_importer.html',
|
||||||
|
model: this
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -67,13 +67,10 @@
|
|||||||
Create New
|
Create New
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<form class="pull-left p-r-1">
|
<button class="btn btn-inverse pull-left" ng-click="ctrl.import()" ng-show="ctrl.contextSrv.isEditor">
|
||||||
<input type="file" id="dashupload" dash-upload name="dashupload" class="hide"/>
|
|
||||||
<label class="btn btn-inverse" for="dashupload">
|
|
||||||
<i class="fa fa-upload"></i>
|
<i class="fa fa-upload"></i>
|
||||||
Upload Dashboard
|
Import
|
||||||
</label>
|
</button>
|
||||||
</form>
|
|
||||||
|
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,6 +5,7 @@ import config from 'app/core/config';
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import coreModule from '../../core_module';
|
import coreModule from '../../core_module';
|
||||||
|
import {DashImporter} from '../dash_importer/dash_importer';
|
||||||
|
|
||||||
export class SearchCtrl {
|
export class SearchCtrl {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@ -151,6 +152,10 @@ export class SearchCtrl {
|
|||||||
newDashboard() {
|
newDashboard() {
|
||||||
this.$location.url('dashboard/new');
|
this.$location.url('dashboard/new');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
import() {
|
||||||
|
new DashImporter(this.backendSrv, this.$location).run();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function searchDirective() {
|
export function searchDirective() {
|
||||||
|
@ -11,19 +11,21 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
|
<div ng-if="activeStep">
|
||||||
|
|
||||||
<table class="filter-table">
|
</div>
|
||||||
<tbody>
|
|
||||||
<tr ng-repeat="step in model.steps">
|
|
||||||
<td>{{step.name}}</td>
|
|
||||||
<td>{{step.status}}</td>
|
|
||||||
<td width="1%">
|
|
||||||
<i class="fa fa-check" style="color: #39A039"></i>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
|
<!-- <table class="filter-table"> -->
|
||||||
|
<!-- <tbody> -->
|
||||||
|
<!-- <tr ng-repeat="step in model.steps"> -->
|
||||||
|
<!-- <td>{{step.name}}</td> -->
|
||||||
|
<!-- <td>{{step.status}}</td> -->
|
||||||
|
<!-- <td width="1%"> -->
|
||||||
|
<!-- <i class="fa fa-check" style="color: #39A039"></i> -->
|
||||||
|
<!-- </td> -->
|
||||||
|
<!-- </tr> -->
|
||||||
|
<!-- </tbody> -->
|
||||||
|
<!-- </table> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -8,40 +8,50 @@ import coreModule from 'app/core/core_module';
|
|||||||
import appEvents from 'app/core/app_events';
|
import appEvents from 'app/core/app_events';
|
||||||
|
|
||||||
export class WizardSrv {
|
export class WizardSrv {
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class WizardStep {
|
export interface WizardStep {
|
||||||
name: string;
|
name: string;
|
||||||
fn: any;
|
type: string;
|
||||||
|
process: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SelectOptionStep {
|
||||||
|
type: string;
|
||||||
|
name: string;
|
||||||
|
fulfill: any;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.type = 'select';
|
||||||
|
}
|
||||||
|
|
||||||
|
process() {
|
||||||
|
return new Promise((fulfill, reject) => {
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class WizardFlow {
|
export class WizardFlow {
|
||||||
name: string;
|
name: string;
|
||||||
steps: WizardStep[];
|
steps: WizardStep[];
|
||||||
reject: any;
|
|
||||||
fulfill: any;
|
|
||||||
|
|
||||||
constructor(name) {
|
constructor(name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.steps = [];
|
this.steps = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
addStep(name, stepFn) {
|
addStep(step) {
|
||||||
this.steps.push({
|
this.steps.push(step);
|
||||||
name: name,
|
|
||||||
fn: stepFn
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
next(index) {
|
next(index) {
|
||||||
var step = this.steps[0];
|
var step = this.steps[0];
|
||||||
|
|
||||||
return step.fn().then(() => {
|
return step.process().then(() => {
|
||||||
if (this.steps.length === index+1) {
|
if (this.steps.length === index+1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ export class UtilSrv {
|
|||||||
|
|
||||||
Promise.resolve(modal).then(function(modalEl) {
|
Promise.resolve(modal).then(function(modalEl) {
|
||||||
modalEl.modal('show');
|
modalEl.modal('show');
|
||||||
|
options.scope.model.dismiss = options.scope.dismiss;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,34 +3,22 @@
|
|||||||
import kbn from 'app/core/utils/kbn';
|
import kbn from 'app/core/utils/kbn';
|
||||||
import coreModule from 'app/core/core_module';
|
import coreModule from 'app/core/core_module';
|
||||||
|
|
||||||
import {WizardFlow} from 'app/core/core';
|
var template = `
|
||||||
|
<input type="file" id="dashupload" name="dashupload" class="hide"/>
|
||||||
var wnd: any = window;
|
<label class="btn btn-secondary" for="dashupload">
|
||||||
|
<i class="fa fa-upload"></i>
|
||||||
class DashboardImporter {
|
Upload .json File
|
||||||
|
</label>
|
||||||
prepareForImport(dash) {
|
`;
|
||||||
dash.id = null;
|
|
||||||
|
|
||||||
var wizard = new WizardFlow('Import Dashboard');
|
|
||||||
|
|
||||||
wizard.addStep("Importing dashboard", function() {
|
|
||||||
return new Promise(done => {
|
|
||||||
setTimeout(done, 2000);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return wizard.start().then(() => {
|
|
||||||
return dash;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
function uploadDashboardDirective(timer, alertSrv, $location) {
|
function uploadDashboardDirective(timer, alertSrv, $location) {
|
||||||
return {
|
return {
|
||||||
restrict: 'A',
|
restrict: 'E',
|
||||||
|
template: template,
|
||||||
|
scope: {
|
||||||
|
onUpload: '&',
|
||||||
|
},
|
||||||
link: function(scope) {
|
link: function(scope) {
|
||||||
function file_selected(evt) {
|
function file_selected(evt) {
|
||||||
var files = evt.target.files; // FileList object
|
var files = evt.target.files; // FileList object
|
||||||
@ -45,15 +33,7 @@ function uploadDashboardDirective(timer, alertSrv, $location) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var importer = new DashboardImporter();
|
scope.onUpload({dash: dash});
|
||||||
importer.prepareForImport(dash).then(modified => {
|
|
||||||
wnd.grafanaImportDashboard = modified;
|
|
||||||
var title = kbn.slugifyForUrl(dash.title);
|
|
||||||
|
|
||||||
scope.$apply(function() {
|
|
||||||
$location.path('/dashboard-import/' + title);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -63,6 +43,8 @@ function uploadDashboardDirective(timer, alertSrv, $location) {
|
|||||||
reader.readAsText(f);
|
reader.readAsText(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var wnd: any = window;
|
||||||
// Check for the various File API support.
|
// Check for the various File API support.
|
||||||
if (wnd.File && wnd.FileReader && wnd.FileList && wnd.Blob) {
|
if (wnd.File && wnd.FileReader && wnd.FileList && wnd.Blob) {
|
||||||
// Something
|
// Something
|
||||||
|
@ -235,7 +235,7 @@ $paginationActiveBackground: $blue;
|
|||||||
$state-warning-text: darken(#c09853, 10%);
|
$state-warning-text: darken(#c09853, 10%);
|
||||||
$state-warning-bg: $brand-warning;
|
$state-warning-bg: $brand-warning;
|
||||||
|
|
||||||
$errorText: #b94a48;
|
$errorText: #E84D4D;
|
||||||
$errorBackground: $btn-danger-bg;
|
$errorBackground: $btn-danger-bg;
|
||||||
|
|
||||||
$successText: #468847;
|
$successText: #468847;
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
input[type=text].ng-dirty.ng-invalid {
|
input[type=text].ng-dirty.ng-invalid {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input.validation-error,
|
||||||
input.ng-dirty.ng-invalid {
|
input.ng-dirty.ng-invalid {
|
||||||
box-shadow: inset 0 0px 5px $red;
|
box-shadow: inset 0 0px 5px $red;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user