mirror of
https://github.com/valitydev/redash.git
synced 2024-11-06 17:15:17 +00:00
getredash/redash#2642 Date/Time parameters: dynamic default value (current date/time)
This commit is contained in:
parent
d70bcfd615
commit
b0f0b49d1c
@ -5,10 +5,6 @@
|
||||
"transform-object-assign",
|
||||
["babel-plugin-transform-builtin-extend", {
|
||||
"globals": ["Error"]
|
||||
}],
|
||||
["import", {
|
||||
"libraryName": "antd",
|
||||
"style": true
|
||||
}]
|
||||
]
|
||||
}
|
||||
|
@ -2,7 +2,10 @@ import moment from 'moment';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { react2angular } from 'react2angular';
|
||||
import { DatePicker } from 'antd';
|
||||
import DatePicker from 'antd/lib/date-picker';
|
||||
import 'antd/lib/style/core/iconfont.less';
|
||||
import 'antd/lib/input/style/index.less';
|
||||
import 'antd/lib/date-picker/style/index.less';
|
||||
|
||||
function DateInput({
|
||||
value,
|
||||
@ -22,12 +25,18 @@ function DateInput({
|
||||
}
|
||||
|
||||
DateInput.propTypes = {
|
||||
value: PropTypes.instanceOf(Date),
|
||||
value: (props, propName, componentName) => {
|
||||
const value = props[propName];
|
||||
if ((value !== null) && !moment.isMoment(props[propName])) {
|
||||
return new Error('Prop `' + propName + '` supplied to `' + componentName +
|
||||
'` should be a Moment.js instance.');
|
||||
}
|
||||
},
|
||||
onSelect: PropTypes.func,
|
||||
};
|
||||
|
||||
DateInput.defaultProps = {
|
||||
value: Date.now(),
|
||||
value: null,
|
||||
onSelect: () => {},
|
||||
};
|
||||
|
||||
|
@ -2,7 +2,10 @@ import moment from 'moment';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { react2angular } from 'react2angular';
|
||||
import { DatePicker } from 'antd';
|
||||
import DatePicker from 'antd/lib/date-picker';
|
||||
import 'antd/lib/style/core/iconfont.less';
|
||||
import 'antd/lib/input/style/index.less';
|
||||
import 'antd/lib/date-picker/style/index.less';
|
||||
|
||||
function DateTimeInput({
|
||||
value,
|
||||
@ -25,13 +28,19 @@ function DateTimeInput({
|
||||
}
|
||||
|
||||
DateTimeInput.propTypes = {
|
||||
value: PropTypes.instanceOf(Date),
|
||||
value: (props, propName, componentName) => {
|
||||
const value = props[propName];
|
||||
if ((value !== null) && !moment.isMoment(props[propName])) {
|
||||
return new Error('Prop `' + propName + '` supplied to `' + componentName +
|
||||
'` should be a Moment.js instance.');
|
||||
}
|
||||
},
|
||||
withSeconds: PropTypes.bool,
|
||||
onSelect: PropTypes.func,
|
||||
};
|
||||
|
||||
DateTimeInput.defaultProps = {
|
||||
value: Date.now(),
|
||||
value: null,
|
||||
withSeconds: false,
|
||||
onSelect: () => {},
|
||||
};
|
||||
|
@ -26,6 +26,12 @@
|
||||
Global
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group" ng-if="['date', 'datetime-local', 'datetime-with-seconds'].indexOf($ctrl.parameter.type) >= 0">
|
||||
<label>
|
||||
<input type="checkbox" class="form-inline" ng-model="$ctrl.parameter.useCurrentDateTime">
|
||||
Use Today/Now as default value if no other value is set
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group" ng-if="$ctrl.parameter.type === 'enum'">
|
||||
<label>Dropdown List Values (newline delimited)</label>
|
||||
<textarea class="form-control" rows="3" ng-model="$ctrl.parameter.enumOptions"></textarea>
|
||||
|
@ -11,12 +11,12 @@
|
||||
<i class="zmdi zmdi-settings"></i>
|
||||
</button>
|
||||
<span ng-switch="param.type">
|
||||
<date-time-input ng-switch-when="datetime-with-seconds" value="param.ngModel" with-seconds="true"
|
||||
on-select="param.updateValue"></date-time-input>
|
||||
<date-time-input ng-switch-when="datetime-local" value="param.ngModel"
|
||||
on-select="param.updateValue"></date-time-input>
|
||||
<date-input ng-switch-when="date" value="param.ngModel"
|
||||
on-select="param.updateValue"></date-input>
|
||||
<date-time-input ng-switch-when="datetime-with-seconds" value="param.normalizedValue"
|
||||
on-select="param.setValue"></date-time-input>
|
||||
<date-time-input ng-switch-when="datetime-local" value="param.normalizedValue"
|
||||
on-select="param.setValue"></date-time-input>
|
||||
<date-input ng-switch-when="date" value="param.normalizedValue"
|
||||
on-select="param.setValue"></date-input>
|
||||
<span ng-switch-when="enum">
|
||||
<select ng-model="param.value" class="form-control">
|
||||
<option ng-repeat="option in extractEnumOptions(param.enumOptions)" value="{{option}}">{{option}}</option>
|
||||
|
@ -1,10 +1,25 @@
|
||||
import moment from 'moment';
|
||||
import debug from 'debug';
|
||||
import Mustache from 'mustache';
|
||||
import { each, zipObject, isEmpty, map, filter, includes, union, uniq, has } from 'lodash';
|
||||
import {
|
||||
each, zipObject, isEmpty, map, filter, includes, union, uniq, has,
|
||||
isNull, isUndefined,
|
||||
} from 'lodash';
|
||||
|
||||
const logger = debug('redash:services:query');
|
||||
|
||||
const DATETIME_FORMATS = {
|
||||
// eslint-disable-next-line quote-props
|
||||
'date': 'YYYY-MM-DD',
|
||||
'datetime-local': 'YYYY-MM-DD HH:mm',
|
||||
'datetime-with-seconds': 'YYYY-MM-DD HH:mm:ss',
|
||||
};
|
||||
|
||||
function normalizeNumericValue(value, defaultValue = null) {
|
||||
const result = parseFloat(value);
|
||||
return isFinite(result) ? result : defaultValue;
|
||||
}
|
||||
|
||||
function collectParams(parts) {
|
||||
let parameters = [];
|
||||
|
||||
@ -24,43 +39,69 @@ class Parameter {
|
||||
this.title = parameter.title;
|
||||
this.name = parameter.name;
|
||||
this.type = parameter.type;
|
||||
this.value = parameter.value;
|
||||
this.useCurrentDateTime = parameter.useCurrentDateTime;
|
||||
this.global = parameter.global;
|
||||
this.enumOptions = parameter.enumOptions;
|
||||
this.queryId = parameter.queryId;
|
||||
|
||||
// method to update parameter value from date/time picker component
|
||||
// (react does not support two-way binding with `ngModel`)
|
||||
this.updateValue = (value) => {
|
||||
this.ngModel = value;
|
||||
};
|
||||
// validate value and init internal state
|
||||
this.setValue(parameter.value);
|
||||
|
||||
// explicitly bind it to `this` to allow passing it as callback to Ant's
|
||||
// DatePicker component
|
||||
this.setValue = this.setValue.bind(this);
|
||||
}
|
||||
|
||||
get ngModel() {
|
||||
if (this.type === 'date' || this.type === 'datetime-local' || this.type === 'datetime-with-seconds') {
|
||||
this.$$value = this.$$value || moment(this.value).toDate();
|
||||
return this.$$value;
|
||||
} else if (this.type === 'number') {
|
||||
this.$$value = this.$$value || parseInt(this.value, 10);
|
||||
return this.$$value;
|
||||
get isEmpty() {
|
||||
return isNull(this.getValue());
|
||||
}
|
||||
|
||||
getValue() {
|
||||
const isEmptyValue = isNull(this.value) || isUndefined(this.value) || (this.value === '');
|
||||
if (isEmptyValue) {
|
||||
if (
|
||||
includes(['date', 'datetime-local', 'datetime-with-seconds'], this.type) &&
|
||||
this.useCurrentDateTime
|
||||
) {
|
||||
return moment().format(DATETIME_FORMATS[this.type]);
|
||||
}
|
||||
return null; // normalize empty value
|
||||
}
|
||||
if (this.type === 'number') {
|
||||
return normalizeNumericValue(this.value, null); // normalize empty value
|
||||
}
|
||||
return this.value;
|
||||
}
|
||||
|
||||
set ngModel(value) {
|
||||
if (value && this.type === 'date') {
|
||||
this.value = moment(value).format('YYYY-MM-DD');
|
||||
this.$$value = moment(this.value).toDate();
|
||||
} else if (value && this.type === 'datetime-local') {
|
||||
this.value = moment(value).format('YYYY-MM-DD HH:mm');
|
||||
this.$$value = moment(this.value).toDate();
|
||||
} else if (value && this.type === 'datetime-with-seconds') {
|
||||
this.value = moment(value).format('YYYY-MM-DD HH:mm:ss');
|
||||
this.$$value = moment(this.value).toDate();
|
||||
setValue(value) {
|
||||
if (includes(['date', 'datetime-local', 'datetime-with-seconds'], this.type)) {
|
||||
value = moment(value);
|
||||
if (value.isValid()) {
|
||||
this.value = value.format(DATETIME_FORMATS[this.type]);
|
||||
this.$$value = value;
|
||||
} else {
|
||||
this.value = this.$$value = value;
|
||||
this.value = null;
|
||||
this.$$value = null;
|
||||
}
|
||||
} else if (this.type === 'number') {
|
||||
this.value = value;
|
||||
this.$$value = normalizeNumericValue(value, null);
|
||||
} else {
|
||||
this.value = value;
|
||||
this.$$value = value;
|
||||
}
|
||||
}
|
||||
|
||||
get normalizedValue() {
|
||||
return this.$$value;
|
||||
}
|
||||
|
||||
// TODO: Remove this property when finally moved to React
|
||||
get ngModel() {
|
||||
return this.normalizedValue;
|
||||
}
|
||||
set ngModel(value) {
|
||||
this.setValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,7 +171,7 @@ class Parameters {
|
||||
}
|
||||
|
||||
getMissing() {
|
||||
return map(filter(this.get(), p => p.value === null || p.value === ''), i => i.title);
|
||||
return map(filter(this.get(), p => p.isEmpty), i => i.title);
|
||||
}
|
||||
|
||||
isRequired() {
|
||||
@ -139,7 +180,7 @@ class Parameters {
|
||||
|
||||
getValues() {
|
||||
const params = this.get();
|
||||
return zipObject(map(params, i => i.name), map(params, i => i.value));
|
||||
return zipObject(map(params, i => i.name), map(params, i => i.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
|
44
package-lock.json
generated
44
package-lock.json
generated
@ -82,41 +82,6 @@
|
||||
"@babel/types": "7.0.0-beta.44"
|
||||
}
|
||||
},
|
||||
"@babel/helper-module-imports": {
|
||||
"version": "7.0.0-beta.54",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0-beta.54.tgz",
|
||||
"integrity": "sha1-wtjhT/A0Ilv0MTVtt370Z7jTWqw=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "7.0.0-beta.54",
|
||||
"lodash": "^4.17.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/types": {
|
||||
"version": "7.0.0-beta.54",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.54.tgz",
|
||||
"integrity": "sha1-AlrWhJL+1ULBPxTFeaRMhI5TEGM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"esutils": "^2.0.2",
|
||||
"lodash": "^4.17.5",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"esutils": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
|
||||
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
|
||||
"dev": true
|
||||
},
|
||||
"to-fast-properties": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/helper-split-export-declaration": {
|
||||
"version": "7.0.0-beta.44",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.44.tgz",
|
||||
@ -1219,15 +1184,6 @@
|
||||
"babel-runtime": "^6.22.0"
|
||||
}
|
||||
},
|
||||
"babel-plugin-import": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-import/-/babel-plugin-import-1.8.0.tgz",
|
||||
"integrity": "sha512-5Aw8aZnJPuhJdumK6mS2ZRlfmGaBIKm/h6dw5uS0bkRMTqwHespRG3NeN9x9TB4W38I16ZXGGlHHz+8Gt5/shQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-module-imports": "^7.0.0-beta.34"
|
||||
}
|
||||
},
|
||||
"babel-plugin-syntax-async-functions": {
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz",
|
||||
|
@ -80,7 +80,6 @@
|
||||
"babel-eslint": "^8.2.3",
|
||||
"babel-loader": "^7.1.2",
|
||||
"babel-plugin-angularjs-annotate": "^0.8.2",
|
||||
"babel-plugin-import": "^1.8.0",
|
||||
"babel-plugin-transform-builtin-extend": "^1.1.2",
|
||||
"babel-plugin-transform-class-properties": "^6.24.1",
|
||||
"babel-plugin-transform-object-assign": "^6.22.0",
|
||||
|
Loading…
Reference in New Issue
Block a user