Коммит
4a95285161
|
@ -385,10 +385,76 @@ return {
|
|||
return { queryTimespan, granularity };
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "filters",
|
||||
type: "ApplicationInsights/Query",
|
||||
dependencies: {
|
||||
timespan: "timespan",
|
||||
queryTimespan: "timespan:queryTimespan",
|
||||
granularity: "timespan:granularity"
|
||||
},
|
||||
params: {
|
||||
table: "customEvents",
|
||||
queries: {
|
||||
filterChannels: {
|
||||
query: () => `` +
|
||||
` where name == 'Activity' | ` +
|
||||
` extend channel=customDimensions.channel | ` +
|
||||
` summarize channel_count=count() by tostring(channel) | ` +
|
||||
` order by channel_count`,
|
||||
mappings: {
|
||||
channel: (val) => val || "unknown",
|
||||
channel_count: (val) => val || 0
|
||||
},
|
||||
calculated: (filterChannels) => {
|
||||
const filters = filterChannels.map((x) => x.channel);
|
||||
let { selectedValues } = filterChannels;
|
||||
if (selectedValues === undefined) {
|
||||
selectedValues = [];
|
||||
}
|
||||
return {
|
||||
"channels-count": filterChannels,
|
||||
"channels-filters": filters,
|
||||
"channels-selected": selectedValues,
|
||||
};
|
||||
}
|
||||
},
|
||||
filterIntents: {
|
||||
query: () => `` +
|
||||
` extend intent=customDimensions.intent, cslen = customDimensions.callstack_length | ` +
|
||||
` where name startswith 'message.intent' and (cslen == 0 or strlen(cslen) == 0) and strlen(intent) > 0 | ` +
|
||||
` summarize intent_count=count() by tostring(intent) | ` +
|
||||
` order by intent_count`,
|
||||
mappings: {
|
||||
intent: (val) => val || "unknown",
|
||||
intent_count: (val) => val || 0
|
||||
},
|
||||
calculated: (filterIntents) => {
|
||||
const intents = filterIntents.map((x) => x.intent);
|
||||
let { selectedValues } = filterIntents;
|
||||
if (selectedValues === undefined) {
|
||||
selectedValues = [];
|
||||
}
|
||||
return {
|
||||
"intents-count": filterIntents,
|
||||
"intents-filters": intents,
|
||||
"intents-selected": selectedValues,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'ai',
|
||||
type: "ApplicationInsights/Query",
|
||||
dependencies: { timespan: "timespan", queryTimespan: "timespan:queryTimespan", granularity: "timespan:granularity" },
|
||||
dependencies: {
|
||||
timespan: "timespan",
|
||||
queryTimespan: "timespan:queryTimespan",
|
||||
granularity: "timespan:granularity",
|
||||
selectedChannels: "filters:channels-selected",
|
||||
selectedIntents: "filters:intents-selected"
|
||||
},
|
||||
params: {
|
||||
table: "customEvents",
|
||||
queries: {
|
||||
|
@ -401,6 +467,10 @@ return {
|
|||
"successful": (val) => val === 'true',
|
||||
"event_count": (val) => val || 0
|
||||
},
|
||||
filters: [{
|
||||
dependency: "selectedChannels",
|
||||
queryProperty: "customDimensions.channel"
|
||||
}],
|
||||
calculated: (conversions) => {
|
||||
|
||||
// Conversion Handling
|
||||
|
@ -438,6 +508,10 @@ return {
|
|||
"channel": (val) => val || "unknown",
|
||||
"count": (val) => val || 0,
|
||||
},
|
||||
filters: [{
|
||||
dependency: "selectedChannels",
|
||||
queryProperty: "customDimensions.channel"
|
||||
}],
|
||||
calculated: (timeline, dependencies) => {
|
||||
|
||||
// Timeline handling
|
||||
|
@ -489,6 +563,10 @@ return {
|
|||
"intent": (val) => val || "Unknown",
|
||||
"count": (val) => val || 0,
|
||||
},
|
||||
filters: [{
|
||||
dependency: "selectedIntents",
|
||||
queryProperty: "customDimensions.intent"
|
||||
}],
|
||||
calculated: (intents) => {
|
||||
return {
|
||||
"intents-bars": [ 'count' ]
|
||||
|
@ -497,6 +575,10 @@ return {
|
|||
},
|
||||
users: {
|
||||
query: `summarize totalUsers=count() by user_Id`,
|
||||
filters: [{
|
||||
dependency: "selectedChannels",
|
||||
queryProperty: "customDimensions.channel"
|
||||
}],
|
||||
calculated: (users) => {
|
||||
let result = 0;
|
||||
if (users.length === 1 && users[0].totalUsers > 0) {
|
||||
|
@ -520,6 +602,10 @@ return {
|
|||
duration: (val) => val || 0,
|
||||
channel: (val) => val || 'unknown'
|
||||
},
|
||||
filters: [{
|
||||
dependency: "selectedChannels",
|
||||
queryProperty: "customDimensions.channel"
|
||||
}],
|
||||
calculated: (channelActivity) => {
|
||||
var groupedValues = _.chain(channelActivity).groupBy('channel').value();
|
||||
return {
|
||||
|
@ -605,6 +691,34 @@ return {
|
|||
dependencies: { selectedValue: "timespan", values: "timespan:values" },
|
||||
actions: { onChange: "timespan:updateSelectedValue" },
|
||||
first: true
|
||||
},
|
||||
{
|
||||
type: "MenuFilter",
|
||||
title: "Channels",
|
||||
subtitle: "Select channels",
|
||||
icon: "forum",
|
||||
dependencies: {
|
||||
selectedValues: "filters:channels-selected",
|
||||
values: "filters:channels-filters"
|
||||
},
|
||||
actions: {
|
||||
onChange: "filters:updateSelectedValues:channels-selected"
|
||||
},
|
||||
first: true
|
||||
},
|
||||
{
|
||||
type: "MenuFilter",
|
||||
title: "Intents",
|
||||
subtitle: "Select intents",
|
||||
icon: "textsms",
|
||||
dependencies: {
|
||||
selectedValues: "filters:intents-selected",
|
||||
values: "filters:intents-filters"
|
||||
},
|
||||
actions: {
|
||||
onChange: "filters:updateSelectedValues:intents-selected"
|
||||
},
|
||||
first: true
|
||||
}
|
||||
],
|
||||
elements: [
|
||||
|
|
|
@ -78,6 +78,9 @@ export default class ElementConnector {
|
|||
key={idx}
|
||||
dependencies={element.dependencies}
|
||||
actions={element.actions}
|
||||
title={element.title}
|
||||
subtitle={element.subtitle}
|
||||
icon={element.icon}
|
||||
/>
|
||||
)
|
||||
});
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
import * as React from 'react';
|
||||
import { GenericComponent } from './GenericComponent';
|
||||
import Checkbox from 'react-md/lib/SelectionControls/Checkbox';
|
||||
|
||||
const style = {
|
||||
checkbox: {
|
||||
float: "left",
|
||||
paddingTop: "24px"
|
||||
}
|
||||
}
|
||||
|
||||
export default class CheckboxFilter extends GenericComponent<any, any> {
|
||||
|
||||
state = {
|
||||
values: [],
|
||||
selectedValues: []
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.onChange = this.onChange.bind(this);
|
||||
}
|
||||
|
||||
onChange(newValue, checked, event) {
|
||||
var { selectedValues } = this.state;
|
||||
let newSelectedValues = selectedValues.slice(0);
|
||||
|
||||
const idx = selectedValues.findIndex((x) => x === newValue);
|
||||
if (idx === -1 && checked) {
|
||||
newSelectedValues.push(newValue);
|
||||
} else if (idx > -1 && !checked) {
|
||||
newSelectedValues.splice(idx, 1);
|
||||
} else {
|
||||
console.warn("Unexpected checked filter state:", newValue, checked);
|
||||
}
|
||||
|
||||
this.trigger('onChange', newSelectedValues);
|
||||
}
|
||||
|
||||
render() {
|
||||
var { title } = this.props;
|
||||
var { selectedValues, values } = this.state;
|
||||
values = values || [];
|
||||
|
||||
let checkboxes = values.map((value, idx) => {
|
||||
return (<Checkbox
|
||||
key={idx}
|
||||
id={idx}
|
||||
name={value}
|
||||
label={value}
|
||||
onChange={this.onChange.bind(null, value)}
|
||||
style={style.checkbox}
|
||||
checked={selectedValues.find((x) => x === value) !== undefined} />);
|
||||
})
|
||||
|
||||
return (
|
||||
<div id="filters">
|
||||
<div style={style.checkbox}><label>{title}</label></div>
|
||||
{checkboxes}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,207 @@
|
|||
import * as React from 'react';
|
||||
import { GenericComponent } from './GenericComponent';
|
||||
import Button from 'react-md/lib/Buttons';
|
||||
import Portal from 'react-md/lib/Helpers/Portal';
|
||||
import AccessibleFakeButton from 'react-md/lib/Helpers/AccessibleFakeButton';
|
||||
import AccessibleFakeInkedButton from 'react-md/lib/Helpers/AccessibleFakeInkedButton';
|
||||
import List from 'react-md/lib/Lists/List';
|
||||
import ListItemControl from 'react-md/lib/Lists/ListItemControl';
|
||||
import Checkbox from 'react-md/lib/SelectionControls/Checkbox';
|
||||
import ListItem from 'react-md/lib/Lists/ListItem';
|
||||
import FontIcon from 'react-md/lib/FontIcons';
|
||||
import './generic.css';
|
||||
|
||||
const styles = {
|
||||
button: {
|
||||
userSelect: 'none',
|
||||
},
|
||||
container: {
|
||||
position: 'relative',
|
||||
float: 'left',
|
||||
zIndex: 17,
|
||||
},
|
||||
animateOpen: {
|
||||
transition: '.3s',
|
||||
transform: 'scale(1.0,1.0)',
|
||||
transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
},
|
||||
animateClose: {
|
||||
transform: 'scale(1.0,0)',
|
||||
transition: '0s',
|
||||
},
|
||||
list: {
|
||||
position: 'absolute',
|
||||
top: '0px',
|
||||
left: '0px',
|
||||
}
|
||||
};
|
||||
|
||||
// using styles from the select field menu
|
||||
const classNames = {
|
||||
menu: ['md-inline-block', 'md-menu-container', 'md-menu-container--menu-below', 'md-select-field-menu',
|
||||
'md-select-field-menu--stretch', 'md-select-field--toolbar', ''],
|
||||
label: ['md-floating-label', 'md-floating-label--floating', ''],
|
||||
};
|
||||
|
||||
export default class MenuFilter extends GenericComponent<any, any> {
|
||||
|
||||
static defaultProps = {
|
||||
title: '',
|
||||
subtitle: 'Select filter',
|
||||
icon: 'more_vert',
|
||||
selectAll: 'Enable filters',
|
||||
selectNone: 'Clear filters'
|
||||
};
|
||||
|
||||
state = {
|
||||
overlay: false,
|
||||
values: [],
|
||||
selectedValues: [],
|
||||
originalSelectedValues: []
|
||||
};
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.toggleOverlay = this.toggleOverlay.bind(this);
|
||||
this.hideOverlay = this.hideOverlay.bind(this);
|
||||
this.selectAll = this.selectAll.bind(this);
|
||||
this.selectNone = this.selectNone.bind(this);
|
||||
}
|
||||
|
||||
toggleOverlay() {
|
||||
const { overlay, selectedValues } = this.state;
|
||||
this.setState({ overlay: !overlay, originalSelectedValues: selectedValues });
|
||||
if (overlay) {
|
||||
this.triggerChanges();
|
||||
}
|
||||
}
|
||||
|
||||
hideOverlay() {
|
||||
this.setState({ overlay: false });
|
||||
this.triggerChanges();
|
||||
}
|
||||
|
||||
triggerChanges() {
|
||||
const { selectedValues } = this.state;
|
||||
if (!this.didSelectionChange()) {
|
||||
return;
|
||||
}
|
||||
this.trigger('onChange', selectedValues);
|
||||
}
|
||||
|
||||
didSelectionChange(): boolean {
|
||||
const { selectedValues, originalSelectedValues } = this.state;
|
||||
if (!selectedValues || !originalSelectedValues) {
|
||||
return false;
|
||||
}
|
||||
if (selectedValues.length !== originalSelectedValues.length
|
||||
|| selectedValues.slice(0).sort().join() !== originalSelectedValues.slice(0).sort().join()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
onChange(newValue: any, checked: boolean, event: any) {
|
||||
var { selectedValues } = this.state;
|
||||
let newSelectedValues = selectedValues.slice(0);
|
||||
const idx = selectedValues.findIndex((x) => x === newValue);
|
||||
if (idx === -1 && checked) {
|
||||
newSelectedValues.push(newValue);
|
||||
} else if (idx > -1 && !checked) {
|
||||
newSelectedValues.splice(idx, 1);
|
||||
} else {
|
||||
console.warn('Unexpected checked filter state:', newValue, checked);
|
||||
}
|
||||
this.setState({ selectedValues: newSelectedValues });
|
||||
}
|
||||
|
||||
selectAll() {
|
||||
this.setState({ selectedValues: this.state.values });
|
||||
}
|
||||
|
||||
selectNone() {
|
||||
this.setState({ selectedValues: [] });
|
||||
}
|
||||
|
||||
render() {
|
||||
var { title, subtitle, icon } = this.props;
|
||||
var { selectedValues, values, overlay } = this.state;
|
||||
values = values || [];
|
||||
let listItems = values.map((value, idx) => {
|
||||
return (
|
||||
<ListItemControl
|
||||
key={idx + title}
|
||||
primaryAction={(
|
||||
<Checkbox
|
||||
id={idx + value}
|
||||
name={value}
|
||||
label={value}
|
||||
onChange={this.onChange.bind(null, value)}
|
||||
checked={selectedValues.find((x) => x === value) !== undefined}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
if (values.length > 1) {
|
||||
const selectAll = this.props.selectAll;
|
||||
const selectNone = this.props.selectNone;
|
||||
const iconAll = <FontIcon>done_all</FontIcon>;
|
||||
const iconNone = <FontIcon disabled>check_box_outline_blank</FontIcon>;
|
||||
listItems.push(<ListItem key="all" primaryText={selectAll} onClick={this.selectAll} rightIcon={iconAll} />);
|
||||
listItems.push(<ListItem key="none" primaryText={selectNone} onClick={this.selectNone} rightIcon={iconNone} />);
|
||||
}
|
||||
|
||||
const paperStyle = overlay ? classNames.menu.join(' ') + 'md-paper md-paper--1' : classNames.menu.join(' ');
|
||||
const labelStyle = overlay ? classNames.label.join(' ') + 'md-floating-label--active' : classNames.label.join(' ');
|
||||
|
||||
const containerStyle = overlay ? { ...styles.container, ...styles.animateOpen }
|
||||
: { ...styles.container, ...styles.animateClose };
|
||||
|
||||
let selectText = subtitle || 'Select';
|
||||
if (selectedValues === undefined) {
|
||||
selectText = subtitle || 'Select';
|
||||
} else if (selectedValues.length === 1) {
|
||||
selectText = selectedValues[0];
|
||||
} else if (selectedValues.length > 1) {
|
||||
selectText = `${selectedValues.length} selected`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="filters">
|
||||
|
||||
<AccessibleFakeInkedButton
|
||||
className={paperStyle}
|
||||
onClick={this.toggleOverlay}
|
||||
aria-haspopup="true"
|
||||
aria-expanded={overlay}
|
||||
style={styles.button}
|
||||
>
|
||||
<label className={labelStyle}>{title}</label>
|
||||
<div className="md-icon-separator md-text-field md-select-field--btn md-text-field--floating-margin">
|
||||
<span className="md-value md-icon-text">{selectText}</span>
|
||||
<FontIcon>arrow_drop_down</FontIcon>
|
||||
</div>
|
||||
</AccessibleFakeInkedButton>
|
||||
|
||||
<div className="md-multiselect-menu" style={containerStyle}>
|
||||
<List className="md-paper md-paper--1" style={styles.list}>
|
||||
{listItems}
|
||||
</List>
|
||||
</div>
|
||||
|
||||
<Portal visible={overlay}>
|
||||
<AccessibleFakeButton
|
||||
className="md-overlay"
|
||||
onClick={this.hideOverlay}
|
||||
/>
|
||||
</Portal>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -60,4 +60,24 @@
|
|||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* MenuFilter */
|
||||
|
||||
@media screen and (min-width: 320px) {
|
||||
.md-multiselect-menu {
|
||||
margin-top: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1025px) {
|
||||
.md-multiselect-menu {
|
||||
margin-top: 58px;
|
||||
}
|
||||
}
|
||||
|
||||
.md-value {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
|
@ -1,10 +1,13 @@
|
|||
import PieData from './PieData';
|
||||
import TextFilter from './TextFilter';
|
||||
import Timeline from './Timeline';
|
||||
import Scatter from './Scatter';
|
||||
import BarData from './BarData';
|
||||
import Area from './Area';
|
||||
import Scorecard from './Scorecard';
|
||||
// filters
|
||||
import TextFilter from './TextFilter';
|
||||
import CheckboxFilter from './CheckboxFilter';
|
||||
import MenuFilter from './MenuFilter';
|
||||
// dialog views
|
||||
import Table from './Table';
|
||||
import Detail from './Detail';
|
||||
|
@ -12,12 +15,14 @@ import SplitPanel from './SplitPanel';
|
|||
|
||||
export default {
|
||||
PieData,
|
||||
TextFilter,
|
||||
Timeline,
|
||||
Scatter,
|
||||
BarData,
|
||||
Area,
|
||||
Scorecard,
|
||||
TextFilter,
|
||||
CheckboxFilter,
|
||||
MenuFilter,
|
||||
Table,
|
||||
Detail,
|
||||
SplitPanel,
|
||||
|
|
|
@ -148,12 +148,17 @@ export class DataSourceConnector {
|
|||
static triggerAction(action: string, params: IStringDictionary, args: IDictionary) {
|
||||
var actionLocation = action.split(':');
|
||||
|
||||
if (actionLocation.length !== 2) {
|
||||
if (actionLocation.length !== 2 && actionLocation.length !== 3) {
|
||||
throw new Error(`Action triggers should be in format of "dataSource:action", this is not met by ${action}`);
|
||||
}
|
||||
|
||||
var dataSourceName = actionLocation[0];
|
||||
var actionName = actionLocation[1];
|
||||
var selectedValuesProperty = "selectedValues";
|
||||
if (actionLocation.length === 3) {
|
||||
selectedValuesProperty = actionLocation[2];
|
||||
args = { [selectedValuesProperty]: args };
|
||||
}
|
||||
|
||||
if (dataSourceName === 'dialog') {
|
||||
|
||||
|
@ -161,7 +166,7 @@ export class DataSourceConnector {
|
|||
|
||||
DialogsActions.openDialog(actionName, extrapolation.dependencies);
|
||||
} else {
|
||||
|
||||
|
||||
var dataSource = DataSourceConnector.dataSources[dataSourceName];
|
||||
if (!dataSource) {
|
||||
throw new Error(`Data source ${dataSourceName} was not found`)
|
||||
|
|
|
@ -74,4 +74,8 @@ export default class ApplicationInsightsEvents extends DataSourcePlugin<IEventsP
|
|||
// return callback(null, ActionsCommon.prepareResult('value', json));
|
||||
// });
|
||||
}
|
||||
|
||||
updateSelectedValues(dependencies, callback) {
|
||||
|
||||
}
|
||||
}
|
|
@ -13,9 +13,15 @@ interface IQueryParams {
|
|||
mappings?: (string|object)[];
|
||||
table?: string;
|
||||
queries?: IDictionary;
|
||||
filters?: Array<IFilterParams>;
|
||||
calculated?: (results: any) => object;
|
||||
}
|
||||
|
||||
interface IFilterParams {
|
||||
dependency: string;
|
||||
queryProperty: string;
|
||||
}
|
||||
|
||||
export default class ApplicationInsightsQuery extends DataSourcePlugin<IQueryParams> {
|
||||
|
||||
type = 'ApplicationInsights-Query';
|
||||
|
@ -41,7 +47,6 @@ export default class ApplicationInsightsQuery extends DataSourcePlugin<IQueryPar
|
|||
* @param {function} callback
|
||||
*/
|
||||
updateDependencies(dependencies) {
|
||||
|
||||
var emptyDependency = _.find(_.keys(this._props.dependencies), dependencyKey => {
|
||||
return typeof dependencies[dependencyKey] === 'undefined';
|
||||
});
|
||||
|
@ -68,30 +73,28 @@ export default class ApplicationInsightsQuery extends DataSourcePlugin<IQueryPar
|
|||
let mappings: Array<any> = [];
|
||||
let queries: IDictionary = {};
|
||||
let table: string = null;
|
||||
let filters: Array<IFilterParams> = params.filters;
|
||||
|
||||
// Checking if this is a single query or a fork query
|
||||
let query: string;
|
||||
let isForked = !params.query && params.table;
|
||||
let isForked = !params.query && !!params.table;
|
||||
|
||||
if (!isForked) {
|
||||
query = this.compileQuery(params.query, dependencies);
|
||||
let queryKey = this._props.id;
|
||||
query = this.compileQueryWithFilters(params.query, dependencies, isForked, queryKey, filters);
|
||||
mappings.push(params.mappings);
|
||||
|
||||
} else {
|
||||
queries = params.queries || {};
|
||||
table = params.table;
|
||||
|
||||
query = ` ${params.table} | fork `;
|
||||
_.keys(queries).forEach(queryKey => {
|
||||
|
||||
_.keys(queries).every(queryKey => {
|
||||
let queryParams = queries[queryKey];
|
||||
filters = queryParams.filters || [];
|
||||
tableNames.push(queryKey);
|
||||
mappings.push(queryParams.mappings);
|
||||
|
||||
let subquery = this.compileQuery(queryParams.query, dependencies);
|
||||
query += ` (${subquery}) `;
|
||||
query += this.compileQueryWithFilters(queryParams.query, dependencies, isForked, queryKey, filters);
|
||||
return true;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
var queryspan = queryTimespan;
|
||||
|
@ -146,6 +149,13 @@ export default class ApplicationInsightsQuery extends DataSourcePlugin<IQueryPar
|
|||
}
|
||||
}
|
||||
|
||||
updateSelectedValues(dependencies: IDictionary, selectedValues: any) {
|
||||
if ( Array.isArray(selectedValues) ){
|
||||
return _.extend(dependencies, {"selectedValues":selectedValues});
|
||||
}
|
||||
return _.extend(dependencies, selectedValues);
|
||||
}
|
||||
|
||||
private mapAllTables(results: IQueryResults, mappings: Array<IDictionary>): any[][] {
|
||||
|
||||
if (!results || !results.Tables || !results.Tables.length) {
|
||||
|
@ -186,6 +196,30 @@ export default class ApplicationInsightsQuery extends DataSourcePlugin<IQueryPar
|
|||
return typeof query === 'function' ? query(dependencies) : query;
|
||||
}
|
||||
|
||||
private compileQueryWithFilters(query: any, dependencies: any, isForked: boolean, queryKey: string, filters: IFilterParams[]): string {
|
||||
let q = this.compileQuery(query, dependencies);
|
||||
// Don't filter a filter query, or no filters specified
|
||||
if (queryKey.startsWith("filter") || filters === undefined || filters.length === 0) {
|
||||
return this.formatQuery(q, isForked);
|
||||
}
|
||||
// Apply selected filters to connected query
|
||||
filters.every((filter) => {
|
||||
const { dependency, queryProperty } = filter;
|
||||
const selectedFilters = dependencies[dependency] || [];
|
||||
if (selectedFilters.length > 0) {
|
||||
const filter = "where " + selectedFilters.map((value) => `${queryProperty}=="${value}"`).join(' or ') + " | ";
|
||||
q = ` ${filter} \n ${q} `;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return this.formatQuery(q, isForked);
|
||||
}
|
||||
|
||||
private formatQuery(query: string, isForked: boolean = true) {
|
||||
return isForked ? ` (${query}) \n\n` : query;
|
||||
}
|
||||
|
||||
private validateParams(props: any, params: any): void {
|
||||
|
||||
if (!props.dependencies.queryTimespan) {
|
||||
|
|
|
@ -18,7 +18,7 @@ export default class Constant extends DataSourcePlugin<IConstantParams> {
|
|||
var props = this._props;
|
||||
var params = options.params;
|
||||
|
||||
props.actions.push.apply(props.actions, [ 'initialize', 'updateSelectedValue' ]);
|
||||
props.actions.push.apply(props.actions, [ 'initialize', 'updateSelectedValue', 'updateSelectedValues' ]);
|
||||
}
|
||||
|
||||
initialize() {
|
||||
|
@ -42,4 +42,8 @@ export default class Constant extends DataSourcePlugin<IConstantParams> {
|
|||
updateSelectedValue(dependencies: IDictionary, selectedValue: any) {
|
||||
return { selectedValue };
|
||||
}
|
||||
|
||||
updateSelectedValues(dependencies: IDictionary, selectedValues: any) {
|
||||
return { selectedValues };
|
||||
}
|
||||
}
|
|
@ -49,7 +49,7 @@ export abstract class DataSourcePlugin<T> implements IDataSourcePlugin {
|
|||
id: '',
|
||||
dependencies: {} as any,
|
||||
dependables: [],
|
||||
actions: [ 'updateDependencies', 'failure' ],
|
||||
actions: [ 'updateDependencies', 'failure', 'updateSelectedValues' ],
|
||||
params: <T>{},
|
||||
calculated: {}
|
||||
};
|
||||
|
@ -68,9 +68,11 @@ export abstract class DataSourcePlugin<T> implements IDataSourcePlugin {
|
|||
props.calculated = options.calculated || {};
|
||||
|
||||
this.updateDependencies = this.updateDependencies.bind(this);
|
||||
this.updateSelectedValues = this.updateSelectedValues.bind(this);
|
||||
}
|
||||
|
||||
abstract updateDependencies (dependencies: IDictionary, args: IDictionary, callback: (result: any) => void): void;
|
||||
abstract updateSelectedValues (dependencies: IDictionary, selectedValues: any, callback: (result: any) => void): void;
|
||||
|
||||
bind (actionClass: any) {
|
||||
actionClass.type = this.type;
|
||||
|
|
|
@ -57,6 +57,9 @@ interface IFilter {
|
|||
type: string
|
||||
dependencies?: { [id: string]: string }
|
||||
actions?: { [id: string]: string }
|
||||
title?: string
|
||||
subtitle?: string
|
||||
icon?: string
|
||||
first: boolean
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
"jsdoc-format": true,
|
||||
"jsx-no-lambda": false,
|
||||
"jsx-no-multiline-js": false,
|
||||
"jsx-boolean-value": false,
|
||||
"label-position": true,
|
||||
"max-line-length": [ true, 120 ],
|
||||
"member-ordering": [
|
||||
|
@ -42,7 +43,7 @@
|
|||
"timeEnd",
|
||||
"trace"
|
||||
],
|
||||
"no-consecutive-blank-lines": true,
|
||||
"no-consecutive-blank-lines": [true],
|
||||
"no-construct": true,
|
||||
"no-debugger": true,
|
||||
"no-duplicate-variable": true,
|
||||
|
@ -53,7 +54,7 @@
|
|||
"no-switch-case-fall-through": true,
|
||||
"no-trailing-whitespace": false,
|
||||
"no-unused-expression": true,
|
||||
"no-use-before-declare": true,
|
||||
"no-use-before-declare": false,
|
||||
"one-line": [
|
||||
true,
|
||||
"check-catch",
|
||||
|
@ -66,7 +67,7 @@
|
|||
"semicolon": [true, "always"],
|
||||
"switch-default": true,
|
||||
|
||||
"trailing-comma": false,
|
||||
"trailing-comma": [false],
|
||||
|
||||
"triple-equals": [ true, "allow-null-check" ],
|
||||
"typedef": [
|
||||
|
|
Загрузка…
Ссылка в новой задаче