Added an 'import dashboard' feature to load a text file into IBEX
This commit is contained in:
Elad Iwanir 2017-06-27 17:04:43 +03:00
Родитель 2d841a684e
Коммит b681596f59
4 изменённых файлов: 133 добавлений и 8 удалений

Просмотреть файл

@ -33,7 +33,7 @@ app.use('/auth', authRouter.router);
app.use('/api', apiRouter.router);
app.use('/cosmosdb', cosmosDBRouter.router);
app.use('/azure', azureRouter.router);
app.use('/graphql', graphQLRouter.router)
app.use('/graphql', graphQLRouter.router);
app.use(express.static(path.resolve(__dirname, '..', 'build')));

Просмотреть файл

@ -290,6 +290,20 @@ router.post('/setup', (req, res) => {
})
});
router.post('/import/dashboards', (req, res) => {
var content = (req.body && req.body.json) || '';
var lowerCaseFileName = req.body.dashboardFileName.toLowerCase();
var modifiedFileName = lowerCaseFileName + '.private.js'
fs.writeFile(path.join(__dirname, '..', 'dashboards', modifiedFileName), req.body.content, err => {
if (err) {
console.error(err);
return res.end(err);
}
res.send({ res: 'Dashboard imported successfully' });
})
});
module.exports = {
router
}

Просмотреть файл

@ -8,6 +8,7 @@ interface IConfigurationsActions {
loadTemplate(id: string): any;
saveConfiguration(dashboard: IDashboardConfig): any;
failure(error: any): void;
submitDashboardFile(content:string, fileName:string): void;
}
class ConfigurationsActions extends AbstractActions implements IConfigurationsActions {
@ -15,6 +16,36 @@ class ConfigurationsActions extends AbstractActions implements IConfigurationsAc
super(alt);
}
submitDashboardFile = (content, dashboardId) => {
return (dispatcher: (json: any) => void) => {
// Replace both 'id' and 'url' with the requested id from the user
var idRegExPattern = /id: \".*\",/i;
var urlRegExPatternt = /url: \".*\",/i;
var updatedContent =
content.replace(idRegExPattern, "id: \"" + dashboardId + "\",")
.replace(urlRegExPatternt, "url: \"" + dashboardId + "\",")
request('/api/import/dashboards', {
method: 'POST',
json: true,
body: { content: updatedContent, dashboardFileName: dashboardId }
},
(error: any, json: any) => {
if (error || (json && json.errors)) {
return this.failure(error || json.errors);
}
// redirect to the newly imported dashboard
window.location.replace('dashboard/' + dashboardId);
return dispatcher(json);
}
);
};
};
loadConfiguration() {
return (dispatcher: (result: { dashboards: IDashboardConfig[], templates: IDashboardConfig[] }) => void) => {

Просмотреть файл

@ -1,5 +1,7 @@
import * as React from 'react';
import TextField from 'react-md/lib/TextFields';
import Dialog from 'react-md/lib/Dialogs';
import NavigationDrawer from 'react-md/lib/NavigationDrawers';
import Toolbar from 'react-md/lib/Toolbars';
import FontIcon from 'react-md/lib/FontIcons';
@ -12,10 +14,12 @@ import Chip from 'react-md/lib/Chips';
import Menu from 'react-md/lib/Menus/Menu';
import MenuButton from 'react-md/lib/Menus/MenuButton';
import Button from 'react-md/lib/Buttons';
import FileUpload from 'react-md/lib/FileInputs/FileUpload';
import AccountStore from '../../stores/AccountStore';
import AccountActions from '../../actions/AccountActions';
import ConfigurationsActions from '../../actions/ConfigurationsActions';
import ConfigurationsStore from '../../stores/ConfigurationsStore';
import './style.css';
@ -43,6 +47,13 @@ export default class Navbar extends React.Component<any, any> {
dashboards: state.dashboards
});
});
// import dashboard functionality
this.onOpenInfo = this.onOpenInfo.bind(this);
this.onCloseInfo = this.onCloseInfo.bind(this);
this.onSubmitImport = this.onSubmitImport.bind(this);
this.onLoad = this.onLoad.bind(this);
this.setFile = this.setFile.bind(this);
}
componentDidMount() {
@ -55,14 +66,45 @@ export default class Navbar extends React.Component<any, any> {
if (!window['dashboardTemplates']) {
this.setState({ noTemplates: true });
}
},
},
5000
);
}
}
onOpenInfo(html: string) {
this.setState({ importVisible: true });
}
onCloseInfo() {
this.setState({ importVisible: false });
}
updateFileName = (value) => {
this.setState({ fileName: value });
};
onLoad(file, uploadResult) {
const { name, size, type, lastModifiedDate } = file;
this.setState({ fileName: name.substr(0, name.indexOf('.')), content: uploadResult });
}
onSubmitImport() {
var dashboardId = this.state.fileName
ConfigurationsActions.submitDashboardFile(this.state.content, dashboardId);
this.setState({ importVisible: false });
}
setFile(file) {
this.setState({ file });
}
render() {
let { dashboards, noTemplates } = this.state;
let { importVisible } = this.state;
let { file, fileName } = this.state;
let { children, title } = this.props;
let pathname = '/';
try { pathname = window.location.pathname; } catch (e) { }
@ -133,12 +175,50 @@ export default class Navbar extends React.Component<any, any> {
const toolbarActions = [(
<Button
icon
tooltipLabel="Create Dashboard"
href="/"
component={Link}
icon
tooltipLabel="Create Dashboard"
href="/"
component={Link}
>add_box
</Button>), (
</Button>),
(
<Button
icon
tooltipLabel="Import dashboard"
onClick={this.onOpenInfo.bind(this)}
component={Link}
>add_box
</Button>),
<Dialog
id="ImportDashboard"
visible={importVisible}
title="Import dashboard"
dialogStyle={{ width: '50%' }}
modal
actions={[
{ onClick: this.onCloseInfo, primary: false, label: 'Cancel' },
{ onClick: this.onSubmitImport, primary: true, label: 'Submit', disabled: !file },
]}
>
<FileUpload
id="dashboardDefenitionFile"
primary
label="Choose File"
accept="application/json"
onLoadStart={this.setFile}
onLoad={this.onLoad}
/>
<TextField
id="dashboardFileName"
label="Dashboard ID"
value={fileName}
onChange={this.updateFileName}
disabled={!file}
lineDirection="center"
placeholder="Choose an ID for the imported dashboard"
/>
</Dialog>
, (
<MenuButton
id="vert-menu"
icon
@ -168,7 +248,7 @@ export default class Navbar extends React.Component<any, any> {
leftIcon={<FontIcon>lock</FontIcon>}
/>
</MenuButton>
)];
)];
if (noTemplates && !dashboards && window.location.pathname !== '/setup') {
children = (