2021-04-03 11:14:15 +00:00
|
|
|
import React from 'react';
|
2021-04-30 08:10:57 +00:00
|
|
|
import { dataqueryService } from '@/_services';
|
|
|
|
|
import { toast } from 'react-toastify';
|
2021-04-03 11:14:15 +00:00
|
|
|
import 'react-toastify/dist/ReactToastify.css';
|
2021-04-08 13:57:24 +00:00
|
|
|
import SelectSearch, { fuzzySearch } from 'react-select-search';
|
2021-04-26 07:13:20 +00:00
|
|
|
import ReactTooltip from 'react-tooltip';
|
2021-04-30 16:15:42 +00:00
|
|
|
import { allSources } from './QueryEditors';
|
|
|
|
|
import { Transformation } from './Transformation';
|
2021-05-13 13:31:55 +00:00
|
|
|
import { defaultOptions } from './constants';
|
2021-05-22 11:29:27 +00:00
|
|
|
import ReactJson from 'react-json-view';
|
2021-04-04 17:07:03 +00:00
|
|
|
|
2021-04-30 06:31:32 +00:00
|
|
|
const queryNameRegex = new RegExp('^[A-Za-z0-9_-]*$');
|
2021-04-29 16:39:09 +00:00
|
|
|
|
2021-04-30 06:31:32 +00:00
|
|
|
const staticDataSources = [{ kind: 'restapi', id: 'restapi', name: 'REST API' }];
|
2021-04-04 17:07:03 +00:00
|
|
|
|
2021-04-03 11:14:15 +00:00
|
|
|
class QueryManager extends React.Component {
|
2021-04-30 06:31:32 +00:00
|
|
|
constructor(props) {
|
|
|
|
|
super(props);
|
|
|
|
|
|
|
|
|
|
this.state = {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setStateFromProps = (props) => {
|
|
|
|
|
const selectedQuery = props.selectedQuery;
|
|
|
|
|
|
|
|
|
|
this.setState(
|
|
|
|
|
{
|
|
|
|
|
appId: props.appId,
|
|
|
|
|
dataSources: props.dataSources,
|
|
|
|
|
dataQueries: props.dataQueries,
|
|
|
|
|
mode: props.mode,
|
|
|
|
|
currentTab: 1,
|
|
|
|
|
addingQuery: props.addingQuery,
|
|
|
|
|
editingQuery: props.editingQuery,
|
|
|
|
|
queryPaneHeight: props.queryPaneHeight,
|
2021-05-22 11:29:27 +00:00
|
|
|
currentState: props.currentState,
|
|
|
|
|
previewLoading: props.previewLoading,
|
|
|
|
|
queryPreviewData: props.queryPreviewData
|
2021-04-30 06:31:32 +00:00
|
|
|
},
|
|
|
|
|
() => {
|
|
|
|
|
if (this.props.mode === 'edit') {
|
2021-05-02 09:27:26 +00:00
|
|
|
let source = props.dataSources.find((datasource) => datasource.id === selectedQuery.data_source_id);
|
|
|
|
|
if(selectedQuery.kind === 'restapi') source = { kind: 'restapi' };
|
2021-04-30 06:31:32 +00:00
|
|
|
|
|
|
|
|
this.setState({
|
|
|
|
|
options: selectedQuery.options,
|
|
|
|
|
selectedDataSource: source,
|
|
|
|
|
selectedQuery,
|
2021-04-30 08:10:57 +00:00
|
|
|
queryName: selectedQuery.name
|
2021-04-30 06:31:32 +00:00
|
|
|
});
|
2021-04-29 16:39:09 +00:00
|
|
|
} else {
|
2021-04-30 06:31:32 +00:00
|
|
|
this.setState({
|
|
|
|
|
options: {},
|
2021-04-30 08:10:57 +00:00
|
|
|
selectedQuery: null
|
2021-04-30 06:31:32 +00:00
|
|
|
});
|
2021-04-29 16:39:09 +00:00
|
|
|
}
|
2021-04-30 06:31:32 +00:00
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
componentWillReceiveProps(nextProps) {
|
|
|
|
|
this.setStateFromProps(nextProps);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
|
this.setStateFromProps(this.props);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
changeDataSource = (sourceId) => {
|
2021-04-30 08:10:57 +00:00
|
|
|
const source = [...this.state.dataSources, ...staticDataSources].find((datasource) => datasource.id === sourceId);
|
2021-04-30 06:31:32 +00:00
|
|
|
this.setState({
|
|
|
|
|
selectedDataSource: source,
|
|
|
|
|
options: defaultOptions[source.kind],
|
2021-04-30 08:10:57 +00:00
|
|
|
queryName: this.computeQueryName(source.kind)
|
2021-04-30 06:31:32 +00:00
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
switchCurrentTab = (tab) => {
|
|
|
|
|
this.setState({
|
2021-04-30 08:10:57 +00:00
|
|
|
currentTab: tab
|
2021-04-30 06:31:32 +00:00
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
validateQueryName = () => {
|
2021-04-30 08:10:57 +00:00
|
|
|
const {
|
|
|
|
|
queryName, dataQueries, mode, selectedQuery
|
|
|
|
|
} = this.state;
|
2021-04-30 06:31:32 +00:00
|
|
|
|
|
|
|
|
if (mode === 'create') {
|
|
|
|
|
return dataQueries.find((query) => query.name === queryName) === undefined && queryNameRegex.test(queryName);
|
2021-04-29 16:39:09 +00:00
|
|
|
}
|
2021-04-30 08:10:57 +00:00
|
|
|
const existingQuery = dataQueries.find((query) => query.name === queryName);
|
|
|
|
|
if (existingQuery) {
|
|
|
|
|
return existingQuery.id === selectedQuery.id && queryNameRegex.test(queryName);
|
|
|
|
|
}
|
|
|
|
|
return queryNameRegex.test(queryName);
|
2021-04-30 06:31:32 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
computeQueryName = (kind) => {
|
|
|
|
|
const { dataQueries } = this.state;
|
|
|
|
|
const currentQueriesForKind = dataQueries.filter((query) => query.kind === kind);
|
|
|
|
|
let found = false;
|
2021-04-30 12:43:29 +00:00
|
|
|
let newName = '';
|
2021-04-30 06:31:32 +00:00
|
|
|
let currentNumber = currentQueriesForKind.length + 1;
|
|
|
|
|
|
|
|
|
|
while (!found) {
|
2021-04-30 12:43:29 +00:00
|
|
|
newName = `${kind}${currentNumber}`;
|
|
|
|
|
if (dataQueries.find((query) => query.name === newName) === undefined) {
|
2021-04-30 06:31:32 +00:00
|
|
|
found = true;
|
|
|
|
|
}
|
2021-04-30 08:10:57 +00:00
|
|
|
currentNumber += 1;
|
2021-04-04 06:26:23 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-30 12:43:29 +00:00
|
|
|
return newName;
|
2021-04-30 06:31:32 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
createOrUpdateDataQuery = () => {
|
2021-04-30 08:10:57 +00:00
|
|
|
const {
|
|
|
|
|
appId, options, selectedDataSource, mode, queryName
|
|
|
|
|
} = this.state;
|
2021-04-30 06:31:32 +00:00
|
|
|
const kind = selectedDataSource.kind;
|
|
|
|
|
const dataSourceId = selectedDataSource.id;
|
|
|
|
|
|
|
|
|
|
const isQueryNameValid = this.validateQueryName();
|
|
|
|
|
if (!isQueryNameValid) {
|
|
|
|
|
toast.error('Invalid query name. Should be unique and only include letters, numbers and underscore.', {
|
|
|
|
|
hideProgressBar: true,
|
2021-04-30 08:10:57 +00:00
|
|
|
position: 'bottom-center'
|
2021-04-30 06:31:32 +00:00
|
|
|
});
|
|
|
|
|
return;
|
2021-04-03 11:14:15 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-30 06:31:32 +00:00
|
|
|
if (mode === 'edit') {
|
|
|
|
|
this.setState({ isUpdating: true });
|
2021-04-30 08:10:57 +00:00
|
|
|
dataqueryService.update(this.state.selectedQuery.id, queryName, options).then(() => {
|
2021-04-30 06:31:32 +00:00
|
|
|
toast.success('Query Updated', { hideProgressBar: true, position: 'bottom-center' });
|
|
|
|
|
this.setState({ isUpdating: false });
|
|
|
|
|
this.props.dataQueriesChanged();
|
2021-04-30 12:43:29 +00:00
|
|
|
}).catch((error) => {
|
|
|
|
|
this.setState({ isUpdating: false });
|
|
|
|
|
toast.error(error, { hideProgressBar: true, position: 'bottom-center' });
|
2021-04-30 06:31:32 +00:00
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
this.setState({ isCreating: true });
|
2021-04-30 08:10:57 +00:00
|
|
|
dataqueryService.create(appId, queryName, kind, options, dataSourceId).then(() => {
|
2021-04-30 06:31:32 +00:00
|
|
|
toast.success('Query Added', { hideProgressBar: true, position: 'bottom-center' });
|
|
|
|
|
this.setState({ isCreating: false });
|
|
|
|
|
this.props.dataQueriesChanged();
|
2021-04-30 12:43:29 +00:00
|
|
|
}).catch((error) => {
|
|
|
|
|
this.setState({ isCreating: false });
|
|
|
|
|
toast.error(error, { hideProgressBar: true, position: 'bottom-center' });
|
2021-04-30 06:31:32 +00:00
|
|
|
});
|
2021-04-03 11:14:15 +00:00
|
|
|
}
|
2021-04-30 06:31:32 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
optionchanged = (option, value) => {
|
|
|
|
|
this.setState({ options: { ...this.state.options, [option]: value } });
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
optionsChanged = (newOptions) => {
|
|
|
|
|
this.setState({ options: newOptions });
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
toggleOption = (option) => {
|
|
|
|
|
const currentValue = this.state.options[option] ? this.state.options[option] : false;
|
|
|
|
|
this.optionchanged(option, !currentValue);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
renderDataSourceOption = (props, option, snapshot, className) => {
|
|
|
|
|
return (
|
|
|
|
|
<button {...props} className={className} type="button">
|
|
|
|
|
<div className="row">
|
|
|
|
|
<div className="col-md-9">
|
|
|
|
|
<span className="text-muted mx-2">{option.name}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</button>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
render() {
|
|
|
|
|
const {
|
|
|
|
|
dataSources,
|
|
|
|
|
selectedDataSource,
|
|
|
|
|
mode,
|
2021-05-22 11:50:55 +00:00
|
|
|
options,
|
2021-04-30 06:31:32 +00:00
|
|
|
currentTab,
|
|
|
|
|
isUpdating,
|
|
|
|
|
isCreating,
|
|
|
|
|
addingQuery,
|
|
|
|
|
editingQuery,
|
|
|
|
|
selectedQuery,
|
|
|
|
|
queryPaneHeight,
|
|
|
|
|
currentState,
|
2021-05-22 11:29:27 +00:00
|
|
|
queryName,
|
|
|
|
|
previewLoading,
|
|
|
|
|
queryPreviewData
|
2021-04-30 06:31:32 +00:00
|
|
|
} = this.state;
|
|
|
|
|
|
|
|
|
|
let ElementToRender = '';
|
|
|
|
|
|
|
|
|
|
if (selectedDataSource) {
|
|
|
|
|
const sourcecomponentName = selectedDataSource.kind.charAt(0).toUpperCase() + selectedDataSource.kind.slice(1);
|
|
|
|
|
ElementToRender = allSources[sourcecomponentName];
|
2021-04-04 17:07:03 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-30 06:31:32 +00:00
|
|
|
let buttonText = mode === 'edit' ? 'Save' : 'Create';
|
|
|
|
|
const buttonDisabled = isUpdating || isCreating;
|
2021-04-07 07:03:03 +00:00
|
|
|
|
2021-04-30 06:31:32 +00:00
|
|
|
if (isUpdating) {
|
|
|
|
|
buttonText = 'Saving...';
|
|
|
|
|
}
|
|
|
|
|
if (isCreating) {
|
|
|
|
|
buttonText = 'Creating...';
|
2021-04-08 13:57:24 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-30 06:31:32 +00:00
|
|
|
return (
|
|
|
|
|
<div className="query-manager" key={selectedQuery ? selectedQuery.id : ''}>
|
|
|
|
|
<ReactTooltip type="dark" effect="solid" delayShow={250} />
|
|
|
|
|
<div className="row header">
|
|
|
|
|
<div className="col">
|
|
|
|
|
{(addingQuery || editingQuery) && (
|
|
|
|
|
<div className="nav-header">
|
2021-05-22 07:29:44 +00:00
|
|
|
<ul className="nav nav-tabs query-manager-header" data-bs-toggle="tabs">
|
2021-04-30 06:31:32 +00:00
|
|
|
<li className="nav-item">
|
|
|
|
|
<a
|
|
|
|
|
onClick={() => this.switchCurrentTab(1)}
|
|
|
|
|
className={currentTab === 1 ? 'nav-link active' : 'nav-link'}
|
|
|
|
|
>
|
|
|
|
|
General
|
|
|
|
|
</a>
|
|
|
|
|
</li>
|
|
|
|
|
<li className="nav-item">
|
|
|
|
|
<a
|
|
|
|
|
onClick={() => this.switchCurrentTab(2)}
|
|
|
|
|
className={currentTab === 2 ? 'nav-link active' : 'nav-link'}
|
|
|
|
|
>
|
|
|
|
|
Advanced
|
|
|
|
|
</a>
|
|
|
|
|
</li>
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
2021-04-30 16:15:42 +00:00
|
|
|
{((addingQuery || editingQuery) && selectedDataSource) && (
|
2021-04-30 06:31:32 +00:00
|
|
|
<div className="col query-name-field">
|
|
|
|
|
<div className="input-icon" style={{ width: '160px' }}>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
onChange={(e) => this.setState({ queryName: e.target.value })}
|
2021-04-30 08:10:57 +00:00
|
|
|
className="form-control-plaintext form-control-plaintext-sm mt-1"
|
2021-04-30 06:31:32 +00:00
|
|
|
value={queryName}
|
|
|
|
|
style={{ width: '160px' }}
|
2021-05-02 06:17:22 +00:00
|
|
|
autoFocus={false}
|
2021-04-30 06:31:32 +00:00
|
|
|
/>
|
2021-04-30 08:10:57 +00:00
|
|
|
<span className="input-icon-addon">
|
2021-04-30 06:31:32 +00:00
|
|
|
<img src="https://www.svgrepo.com/show/149235/edit.svg" width="12" height="12" />
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
<div className="col-auto">
|
2021-05-22 11:50:55 +00:00
|
|
|
{(addingQuery || editingQuery) && (
|
2021-05-22 11:29:27 +00:00
|
|
|
<span
|
2021-05-22 12:02:11 +00:00
|
|
|
onClick={() => this.props.previewQuery(addingQuery ? { data_source_id: selectedDataSource.id, options: options, kind: selectedDataSource.kind } : selectedQuery)}
|
2021-05-22 11:29:27 +00:00
|
|
|
className={`btn btn-secondary m-1 float-right1 ${
|
|
|
|
|
previewLoading ? ' btn-loading' : ''
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
Preview
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
2021-04-30 06:31:32 +00:00
|
|
|
{(addingQuery || editingQuery) && (
|
|
|
|
|
<button
|
|
|
|
|
onClick={this.createOrUpdateDataQuery}
|
|
|
|
|
disabled={buttonDisabled}
|
|
|
|
|
className="btn btn-primary m-1 float-right"
|
|
|
|
|
>
|
|
|
|
|
{buttonText}
|
|
|
|
|
</button>
|
|
|
|
|
)}
|
|
|
|
|
{queryPaneHeight === '30%' ? (
|
|
|
|
|
<span
|
|
|
|
|
className="btn btn-light m-1"
|
|
|
|
|
onClick={this.props.toggleQueryPaneHeight}
|
|
|
|
|
data-tip="Maximize query editor"
|
|
|
|
|
>
|
2021-05-20 05:06:32 +00:00
|
|
|
<img src="https://www.svgrepo.com/show/310311/arrow-maximize.svg" width="12" height="12" />
|
2021-04-30 06:31:32 +00:00
|
|
|
</span>
|
|
|
|
|
) : (
|
|
|
|
|
<span
|
|
|
|
|
className="btn btn-light m-1"
|
|
|
|
|
onClick={this.props.toggleQueryPaneHeight}
|
|
|
|
|
data-tip="Minimize query editor"
|
|
|
|
|
>
|
|
|
|
|
<img src="https://www.svgrepo.com/show/310476/arrow-minimize.svg" width="12" height="12" />
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{(addingQuery || editingQuery) && (
|
2021-05-13 16:38:13 +00:00
|
|
|
<div className="py-2">
|
2021-04-30 06:31:32 +00:00
|
|
|
{currentTab === 1 && (
|
2021-05-13 07:25:54 +00:00
|
|
|
<div className="row row-deck px-2">
|
2021-04-30 06:31:32 +00:00
|
|
|
{dataSources && mode === 'create' && (
|
|
|
|
|
<div className="datasource-picker mb-2">
|
|
|
|
|
<label className="form-label col-md-2">Datasource</label>
|
|
|
|
|
<SelectSearch
|
|
|
|
|
options={[
|
|
|
|
|
...dataSources.map((source) => {
|
|
|
|
|
return { name: source.name, value: source.id };
|
|
|
|
|
}),
|
|
|
|
|
...staticDataSources.map((source) => {
|
|
|
|
|
return { name: source.name, value: source.id };
|
2021-04-30 08:10:57 +00:00
|
|
|
})
|
2021-04-30 06:31:32 +00:00
|
|
|
]}
|
|
|
|
|
value={selectedDataSource ? selectedDataSource.id : ''}
|
|
|
|
|
search={true}
|
|
|
|
|
onChange={(value) => this.changeDataSource(value)}
|
|
|
|
|
filterOptions={fuzzySearch}
|
|
|
|
|
renderOption={this.renderDataSourceOption}
|
|
|
|
|
placeholder="Select a data source"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{selectedDataSource && (
|
|
|
|
|
<div>
|
2021-05-03 08:10:23 +00:00
|
|
|
<ElementToRender options={this.state.options} optionsChanged={this.optionsChanged} currentState={currentState}/>
|
2021-04-30 16:15:42 +00:00
|
|
|
<hr></hr>
|
|
|
|
|
<div className="mb-3 mt-2">
|
2021-05-03 14:27:32 +00:00
|
|
|
<Transformation changeOption={this.optionchanged} options={this.state.options} currentState={currentState}/>
|
2021-04-30 16:15:42 +00:00
|
|
|
</div>
|
2021-05-22 11:29:27 +00:00
|
|
|
<div className="row header border-top">
|
|
|
|
|
<div className="py-2">
|
|
|
|
|
Preview
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="mb-3 mt-2">
|
|
|
|
|
{previewLoading && <div class="spinner-border text-azure" role="status"></div>}
|
|
|
|
|
{previewLoading === false &&
|
|
|
|
|
<div>
|
|
|
|
|
<ReactJson
|
|
|
|
|
name={false}
|
|
|
|
|
style={{ fontSize: '0.7rem' }}
|
|
|
|
|
enableClipboard={false}
|
|
|
|
|
src={queryPreviewData}
|
|
|
|
|
displayDataTypes={false}
|
|
|
|
|
collapsed={true}
|
|
|
|
|
displayObjectSize={false}
|
|
|
|
|
quotesOnKeys={false}
|
|
|
|
|
sortKeys={true}
|
|
|
|
|
indentWidth={1}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
}
|
|
|
|
|
</div>
|
2021-04-30 06:31:32 +00:00
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{currentTab === 2 && (
|
2021-05-13 16:38:13 +00:00
|
|
|
<div className="advanced-options-container m-2">
|
2021-04-30 06:31:32 +00:00
|
|
|
<label className="form-check form-switch">
|
|
|
|
|
<input
|
|
|
|
|
className="form-check-input"
|
|
|
|
|
type="checkbox"
|
|
|
|
|
onClick={() => this.toggleOption('runOnPageLoad')}
|
|
|
|
|
checked={this.state.options.runOnPageLoad}
|
|
|
|
|
/>
|
|
|
|
|
<span className="form-check-label">Run this query on page load?</span>
|
|
|
|
|
</label>
|
|
|
|
|
<label className="form-check form-switch">
|
|
|
|
|
<input
|
|
|
|
|
className="form-check-input"
|
|
|
|
|
type="checkbox"
|
|
|
|
|
onClick={() => this.toggleOption('requestConfirmation')}
|
|
|
|
|
checked={this.state.options.requestConfirmation}
|
|
|
|
|
/>
|
|
|
|
|
<span className="form-check-label">Request confirmation before running query?</span>
|
|
|
|
|
</label>
|
|
|
|
|
|
|
|
|
|
<hr />
|
|
|
|
|
|
|
|
|
|
<label className="form-check form-switch">
|
|
|
|
|
<input
|
|
|
|
|
className="form-check-input"
|
|
|
|
|
type="checkbox"
|
|
|
|
|
onClick={() => this.toggleOption('showSuccessNotification')}
|
|
|
|
|
checked={this.state.options.showSuccessNotification}
|
|
|
|
|
/>
|
|
|
|
|
<span className="form-check-label">Show notification on success?</span>
|
|
|
|
|
</label>
|
|
|
|
|
|
2021-04-30 08:10:57 +00:00
|
|
|
<div className="row mt-3">
|
|
|
|
|
<div className="col-auto">
|
|
|
|
|
<label className="form-label p-2">Success Message</label>
|
2021-04-30 06:31:32 +00:00
|
|
|
</div>
|
2021-04-30 08:10:57 +00:00
|
|
|
<div className="col">
|
2021-04-30 06:31:32 +00:00
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
disabled={!this.state.options.showSuccessNotification}
|
|
|
|
|
onChange={(e) => this.optionchanged('successMessage', e.target.value)}
|
|
|
|
|
placeholder="Query ran successfully"
|
2021-04-30 08:10:57 +00:00
|
|
|
className="form-control"
|
2021-04-30 06:31:32 +00:00
|
|
|
value={this.state.options.successMessage}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2021-04-03 11:14:15 +00:00
|
|
|
</div>
|
2021-04-26 07:13:20 +00:00
|
|
|
|
2021-04-30 06:31:32 +00:00
|
|
|
<hr />
|
|
|
|
|
|
2021-04-30 08:10:57 +00:00
|
|
|
<div className="row mt-3">
|
|
|
|
|
<div className="col-auto">
|
|
|
|
|
<label className="form-label p-2">Notification duration (s)</label>
|
2021-04-30 06:31:32 +00:00
|
|
|
</div>
|
2021-04-30 08:10:57 +00:00
|
|
|
<div className="col">
|
2021-04-30 06:31:32 +00:00
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
disabled={!this.state.options.showSuccessNotification}
|
|
|
|
|
onChange={(e) => this.optionchanged('notificationDuration', e.target.value)}
|
|
|
|
|
placeholder={5}
|
2021-04-30 08:10:57 +00:00
|
|
|
className="form-control"
|
2021-04-30 06:31:32 +00:00
|
|
|
value={this.state.options.notificationDuration}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2021-04-26 07:13:20 +00:00
|
|
|
</div>
|
2021-04-30 06:31:32 +00:00
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
2021-04-03 11:14:15 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-04 06:46:53 +00:00
|
|
|
export { QueryManager };
|