mirror of
https://github.com/discourse/discourse.git
synced 2025-05-25 00:32:52 +08:00
UX: rework date time input range (#9524)
This commit is contained in:
@ -1,7 +1,7 @@
|
|||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
import { makeArray } from "discourse-common/lib/helpers";
|
import { makeArray } from "discourse-common/lib/helpers";
|
||||||
import { alias, or, and, reads, equal, notEmpty } from "@ember/object/computed";
|
import { alias, or, and, equal, notEmpty } from "@ember/object/computed";
|
||||||
import EmberObject from "@ember/object";
|
import EmberObject, { computed, action } from "@ember/object";
|
||||||
import { next } from "@ember/runloop";
|
import { next } from "@ember/runloop";
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import ReportLoader from "discourse/lib/reports-loader";
|
import ReportLoader from "discourse/lib/reports-loader";
|
||||||
@ -9,6 +9,7 @@ import { exportEntity } from "discourse/lib/export-csv";
|
|||||||
import { outputExportResult } from "discourse/lib/export-result";
|
import { outputExportResult } from "discourse/lib/export-result";
|
||||||
import Report, { SCHEMA_VERSION } from "admin/models/report";
|
import Report, { SCHEMA_VERSION } from "admin/models/report";
|
||||||
import ENV from "discourse-common/config/environment";
|
import ENV from "discourse-common/config/environment";
|
||||||
|
import { isPresent } from "@ember/utils";
|
||||||
|
|
||||||
const TABLE_OPTIONS = {
|
const TABLE_OPTIONS = {
|
||||||
perPage: 8,
|
perPage: 8,
|
||||||
@ -69,8 +70,21 @@ export default Component.extend({
|
|||||||
this._reports = [];
|
this._reports = [];
|
||||||
},
|
},
|
||||||
|
|
||||||
startDate: reads("filters.startDate"),
|
startDate: computed("filters.startDate", function() {
|
||||||
endDate: reads("filters.endDate"),
|
if (this.filters && isPresent(this.filters.startDate)) {
|
||||||
|
return moment(this.filters.startDate, "YYYY-MM-DD");
|
||||||
|
} else {
|
||||||
|
return moment();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
endDate: computed("filters.endDate", function() {
|
||||||
|
if (this.filters && isPresent(this.filters.endDate)) {
|
||||||
|
return moment(this.filters.endDate, "YYYY-MM-DD");
|
||||||
|
} else {
|
||||||
|
return moment();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
didReceiveAttrs() {
|
didReceiveAttrs() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
@ -126,39 +140,18 @@ export default Component.extend({
|
|||||||
return `admin-report-${currentMode.replace(/_/g, "-")}`;
|
return `admin-report-${currentMode.replace(/_/g, "-")}`;
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed("startDate")
|
|
||||||
normalizedStartDate(startDate) {
|
|
||||||
return startDate && typeof startDate.isValid === "function"
|
|
||||||
? moment
|
|
||||||
.utc(startDate.toISOString())
|
|
||||||
.locale("en")
|
|
||||||
.format("YYYYMMDD")
|
|
||||||
: moment(startDate)
|
|
||||||
.locale("en")
|
|
||||||
.format("YYYYMMDD");
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("endDate")
|
|
||||||
normalizedEndDate(endDate) {
|
|
||||||
return endDate && typeof endDate.isValid === "function"
|
|
||||||
? moment
|
|
||||||
.utc(endDate.toISOString())
|
|
||||||
.locale("en")
|
|
||||||
.format("YYYYMMDD")
|
|
||||||
: moment(endDate)
|
|
||||||
.locale("en")
|
|
||||||
.format("YYYYMMDD");
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed(
|
@discourseComputed(
|
||||||
"dataSourceName",
|
"dataSourceName",
|
||||||
"normalizedStartDate",
|
"startDate",
|
||||||
"normalizedEndDate",
|
"endDate",
|
||||||
"filters.customFilters"
|
"filters.customFilters"
|
||||||
)
|
)
|
||||||
reportKey(dataSourceName, startDate, endDate, customFilters) {
|
reportKey(dataSourceName, startDate, endDate, customFilters) {
|
||||||
if (!dataSourceName || !startDate || !endDate) return null;
|
if (!dataSourceName || !startDate || !endDate) return null;
|
||||||
|
|
||||||
|
startDate = startDate.toISOString(true).split("T")[0];
|
||||||
|
endDate = endDate.toISOString(true).split("T")[0];
|
||||||
|
|
||||||
let reportKey = "reports:";
|
let reportKey = "reports:";
|
||||||
reportKey += [
|
reportKey += [
|
||||||
dataSourceName,
|
dataSourceName,
|
||||||
@ -179,33 +172,15 @@ export default Component.extend({
|
|||||||
return reportKey;
|
return reportKey;
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
@action
|
||||||
onChangeEndDate(date) {
|
onChangeDateRange(range) {
|
||||||
const startDate = moment(this.startDate);
|
this.send("refreshReport", {
|
||||||
const newEndDate = moment(date).endOf("day");
|
startDate: range.from,
|
||||||
|
endDate: range.to
|
||||||
if (newEndDate.isSameOrAfter(startDate)) {
|
});
|
||||||
this.set("endDate", newEndDate.format("YYYY-MM-DD"));
|
|
||||||
} else {
|
|
||||||
this.set("endDate", startDate.endOf("day").format("YYYY-MM-DD"));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.send("refreshReport");
|
|
||||||
},
|
|
||||||
|
|
||||||
onChangeStartDate(date) {
|
|
||||||
const endDate = moment(this.endDate);
|
|
||||||
const newStartDate = moment(date).startOf("day");
|
|
||||||
|
|
||||||
if (newStartDate.isSameOrBefore(endDate)) {
|
|
||||||
this.set("startDate", newStartDate.format("YYYY-MM-DD"));
|
|
||||||
} else {
|
|
||||||
this.set("startDate", endDate.startOf("day").format("YYYY-MM-DD"));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.send("refreshReport");
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@action
|
||||||
applyFilter(id, value) {
|
applyFilter(id, value) {
|
||||||
let customFilters = this.get("filters.customFilters") || {};
|
let customFilters = this.get("filters.customFilters") || {};
|
||||||
|
|
||||||
@ -215,38 +190,43 @@ export default Component.extend({
|
|||||||
customFilters[id] = value;
|
customFilters[id] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.attrs.onRefresh({
|
this.send("refreshReport", {
|
||||||
type: this.get("model.type"),
|
|
||||||
startDate: this.startDate,
|
|
||||||
endDate: this.endDate,
|
|
||||||
filters: customFilters
|
filters: customFilters
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
refreshReport() {
|
@action
|
||||||
|
refreshReport(options = {}) {
|
||||||
this.attrs.onRefresh({
|
this.attrs.onRefresh({
|
||||||
type: this.get("model.type"),
|
type: this.get("model.type"),
|
||||||
startDate: this.startDate,
|
startDate:
|
||||||
endDate: this.endDate,
|
typeof options.startDate === "undefined"
|
||||||
filters: this.get("filters.customFilters")
|
? this.startDate
|
||||||
|
: options.startDate,
|
||||||
|
endDate:
|
||||||
|
typeof options.endDate === "undefined" ? this.endDate : options.endDate,
|
||||||
|
filters:
|
||||||
|
typeof options.filters === "undefined"
|
||||||
|
? this.get("filters.customFilters")
|
||||||
|
: options.filters
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@action
|
||||||
exportCsv() {
|
exportCsv() {
|
||||||
const customFilters = this.get("filters.customFilters") || {};
|
const customFilters = this.get("filters.customFilters") || {};
|
||||||
|
|
||||||
exportEntity("report", {
|
exportEntity("report", {
|
||||||
name: this.get("model.type"),
|
name: this.get("model.type"),
|
||||||
start_date: this.startDate,
|
start_date: this.startDate.toISOString(true).split("T")[0],
|
||||||
end_date: this.endDate,
|
end_date: this.endDate.toISOString(true).split("T")[0],
|
||||||
category_id: customFilters.category,
|
category_id: customFilters.category,
|
||||||
group_id: customFilters.group
|
group_id: customFilters.group
|
||||||
}).then(outputExportResult);
|
}).then(outputExportResult);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@action
|
||||||
changeMode(mode) {
|
changeMode(mode) {
|
||||||
this.set("currentMode", mode);
|
this.set("currentMode", mode);
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_computeReport() {
|
_computeReport() {
|
||||||
@ -276,10 +256,8 @@ export default Component.extend({
|
|||||||
if (!this.startDate || !this.endDate) {
|
if (!this.startDate || !this.endDate) {
|
||||||
report = sort(filteredReports)[0];
|
report = sort(filteredReports)[0];
|
||||||
} else {
|
} else {
|
||||||
const reportKey = this.reportKey;
|
|
||||||
|
|
||||||
report = sort(
|
report = sort(
|
||||||
filteredReports.filter(r => r.report_key.includes(reportKey))
|
filteredReports.filter(r => r.report_key.includes(this.reportKey))
|
||||||
)[0];
|
)[0];
|
||||||
|
|
||||||
if (!report) return;
|
if (!report) return;
|
||||||
@ -339,15 +317,15 @@ export default Component.extend({
|
|||||||
let payload = { data: { cache: true, facets } };
|
let payload = { data: { cache: true, facets } };
|
||||||
|
|
||||||
if (this.startDate) {
|
if (this.startDate) {
|
||||||
payload.data.start_date = moment
|
payload.data.start_date = moment(this.startDate)
|
||||||
.utc(this.startDate, "YYYY-MM-DD")
|
.toISOString(true)
|
||||||
.toISOString();
|
.split("T")[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.endDate) {
|
if (this.endDate) {
|
||||||
payload.data.end_date = moment
|
payload.data.end_date = moment(this.endDate)
|
||||||
.utc(this.endDate, "YYYY-MM-DD")
|
.toISOString(true)
|
||||||
.toISOString();
|
.split("T")[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.get("reportOptions.table.limit")) {
|
if (this.get("reportOptions.table.limit")) {
|
||||||
|
@ -13,8 +13,7 @@ export default DiscourseRoute.extend({
|
|||||||
|
|
||||||
params.startDate =
|
params.startDate =
|
||||||
params.start_date ||
|
params.start_date ||
|
||||||
moment
|
moment()
|
||||||
.utc()
|
|
||||||
.subtract(1, "day")
|
.subtract(1, "day")
|
||||||
.subtract(1, "month")
|
.subtract(1, "month")
|
||||||
.startOf("day")
|
.startOf("day")
|
||||||
@ -23,8 +22,7 @@ export default DiscourseRoute.extend({
|
|||||||
|
|
||||||
params.endDate =
|
params.endDate =
|
||||||
params.end_date ||
|
params.end_date ||
|
||||||
moment
|
moment()
|
||||||
.utc()
|
|
||||||
.endOf("day")
|
.endOf("day")
|
||||||
.format("YYYY-MM-DD");
|
.format("YYYY-MM-DD");
|
||||||
delete params.end_date;
|
delete params.end_date;
|
||||||
@ -56,9 +54,13 @@ export default DiscourseRoute.extend({
|
|||||||
onParamsChange(params) {
|
onParamsChange(params) {
|
||||||
const queryParams = {
|
const queryParams = {
|
||||||
type: params.type,
|
type: params.type,
|
||||||
start_date: params.startDate,
|
start_date: params.startDate
|
||||||
|
? params.startDate.toISOString(true).split("T")[0]
|
||||||
|
: null,
|
||||||
filters: params.filters,
|
filters: params.filters,
|
||||||
end_date: params.endDate
|
end_date: params.endDate
|
||||||
|
? params.endDate.toISOString(true).split("T")[0]
|
||||||
|
: null
|
||||||
};
|
};
|
||||||
|
|
||||||
this.transitionTo("adminReports.show", { queryParams });
|
this.transitionTo("adminReports.show", { queryParams });
|
||||||
|
@ -132,26 +132,16 @@
|
|||||||
{{#if showDatesOptions}}
|
{{#if showDatesOptions}}
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<span class="label">
|
<span class="label">
|
||||||
{{i18n "admin.dashboard.reports.start_date"}}
|
{{i18n "admin.dashboard.reports.dates"}}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div class="input">
|
<div class="input">
|
||||||
{{date-input
|
{{date-time-input-range
|
||||||
date=startDate
|
from=startDate
|
||||||
onChange=(action "onChangeStartDate")
|
to=endDate
|
||||||
}}
|
onChange=(action "onChangeDateRange")
|
||||||
</div>
|
showFromTime=false
|
||||||
</div>
|
showToTime=false
|
||||||
|
|
||||||
<div class="control">
|
|
||||||
<span class="label">
|
|
||||||
{{i18n "admin.dashboard.reports.end_date"}}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<div class="input">
|
|
||||||
{{date-input
|
|
||||||
date=endDate
|
|
||||||
onChange=(action "onChangeEndDate")
|
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { schedule } from "@ember/runloop";
|
import { schedule } from "@ember/runloop";
|
||||||
|
import { action } from "@ember/object";
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
/* global Pikaday:true */
|
/* global Pikaday:true */
|
||||||
import loadScript from "discourse/lib/load-script";
|
import loadScript from "discourse/lib/load-script";
|
||||||
@ -25,8 +26,8 @@ export default Component.extend({
|
|||||||
this._loadPikadayPicker(container);
|
this._loadPikadayPicker(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.date && this._picker) {
|
if (this._picker && this.date) {
|
||||||
this._picker.setDate(this.date, true);
|
this._picker.setDate(moment(this.date).toDate(), true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -34,9 +35,12 @@ export default Component.extend({
|
|||||||
didUpdateAttrs() {
|
didUpdateAttrs() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
|
|
||||||
if (this._picker && typeof this.date === "string") {
|
if (this._picker && this.date) {
|
||||||
const [year, month, day] = this.date.split("-").map(x => parseInt(x, 10));
|
this._picker.setDate(moment(this.date).toDate(), true);
|
||||||
this._picker.setDate(new Date(year, month - 1, day), true);
|
}
|
||||||
|
|
||||||
|
if (this._picker && this.relativeDate) {
|
||||||
|
this._picker.setMinDate(moment(this.relativeDate).toDate(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._picker && !this.date) {
|
if (this._picker && !this.date) {
|
||||||
@ -46,13 +50,12 @@ export default Component.extend({
|
|||||||
|
|
||||||
_loadPikadayPicker(container) {
|
_loadPikadayPicker(container) {
|
||||||
loadScript("/javascripts/pikaday.js").then(() => {
|
loadScript("/javascripts/pikaday.js").then(() => {
|
||||||
const defaultOptions = {
|
let defaultOptions = {
|
||||||
field: this.element.querySelector(".date-picker"),
|
field: this.element.querySelector(".date-picker"),
|
||||||
container: container || this.element.querySelector(".picker-container"),
|
container: container || this.element.querySelector(".picker-container"),
|
||||||
bound: container === null,
|
bound: container === null,
|
||||||
format: "LL",
|
format: "LL",
|
||||||
firstDay: 1,
|
firstDay: 1,
|
||||||
trigger: this.element,
|
|
||||||
i18n: {
|
i18n: {
|
||||||
previousMonth: I18n.t("dates.previous_month"),
|
previousMonth: I18n.t("dates.previous_month"),
|
||||||
nextMonth: I18n.t("dates.next_month"),
|
nextMonth: I18n.t("dates.next_month"),
|
||||||
@ -63,8 +66,16 @@ export default Component.extend({
|
|||||||
onSelect: date => this._handleSelection(date)
|
onSelect: date => this._handleSelection(date)
|
||||||
};
|
};
|
||||||
|
|
||||||
this._picker = new Pikaday(Object.assign(defaultOptions, this._opts()));
|
if (this.relativeDate) {
|
||||||
this._picker.setDate(this.date, true);
|
defaultOptions = Object.assign({}, defaultOptions, {
|
||||||
|
minDate: moment(this.relativeDate).toDate()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this._picker = new Pikaday(
|
||||||
|
Object.assign({}, defaultOptions, this._opts())
|
||||||
|
);
|
||||||
|
this._picker.setDate(moment(this.date).toDate(), true);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -79,18 +90,23 @@ export default Component.extend({
|
|||||||
/* do nothing for native */
|
/* do nothing for native */
|
||||||
};
|
};
|
||||||
picker.setDate = date => {
|
picker.setDate = date => {
|
||||||
picker.value = date;
|
picker.value = moment(date).format("YYYY-MM-DD");
|
||||||
|
};
|
||||||
|
picker.setMinDate = date => {
|
||||||
|
picker.min = date;
|
||||||
};
|
};
|
||||||
this._picker = picker;
|
this._picker = picker;
|
||||||
|
|
||||||
|
if (this.date) {
|
||||||
|
picker.setDate(this.date);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_handleSelection(value) {
|
_handleSelection(value) {
|
||||||
if (!this.element || this.isDestroying || this.isDestroyed) return;
|
if (!this.element || this.isDestroying || this.isDestroyed) return;
|
||||||
|
|
||||||
this._picker && this._picker.hide();
|
|
||||||
|
|
||||||
if (this.onChange) {
|
if (this.onChange) {
|
||||||
this.onChange(value);
|
this.onChange(value ? moment(value) : null);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -98,8 +114,8 @@ export default Component.extend({
|
|||||||
_destroy() {
|
_destroy() {
|
||||||
if (this._picker) {
|
if (this._picker) {
|
||||||
this._picker.destroy();
|
this._picker.destroy();
|
||||||
}
|
|
||||||
this._picker = null;
|
this._picker = null;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed()
|
@discourseComputed()
|
||||||
@ -111,9 +127,8 @@ export default Component.extend({
|
|||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
@action
|
||||||
onInput(event) {
|
onChangeDate(event) {
|
||||||
this._picker && this._picker.setDate(event.target.value, true);
|
this._handleSelection(event.target.value);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,30 +1,19 @@
|
|||||||
import { equal } from "@ember/object/computed";
|
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
|
import { action } from "@ember/object";
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
classNames: ["d-date-time-input-range"],
|
classNames: ["d-date-time-input-range"],
|
||||||
|
|
||||||
from: null,
|
from: null,
|
||||||
to: null,
|
to: null,
|
||||||
onChangeTo: null,
|
onChangeTo: null,
|
||||||
onChangeFrom: null,
|
onChangeFrom: null,
|
||||||
currentPanel: "from",
|
toTimeFirst: false,
|
||||||
showFromTime: true,
|
|
||||||
showToTime: true,
|
showToTime: true,
|
||||||
error: null,
|
showFromTime: true,
|
||||||
|
clearable: false,
|
||||||
|
|
||||||
fromPanelActive: equal("currentPanel", "from"),
|
@action
|
||||||
toPanelActive: equal("currentPanel", "to"),
|
onChangeRanges(options, value) {
|
||||||
|
|
||||||
_valid(state) {
|
|
||||||
if (state.to && state.from && state.to < state.from) {
|
|
||||||
return I18n.t("date_time_picker.errors.to_before_from");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
_onChange(options, value) {
|
|
||||||
if (this.onChange) {
|
if (this.onChange) {
|
||||||
const state = {
|
const state = {
|
||||||
from: this.from,
|
from: this.from,
|
||||||
@ -32,22 +21,26 @@ export default Component.extend({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const diff = {};
|
const diff = {};
|
||||||
|
|
||||||
|
if (options.prop === "from") {
|
||||||
|
if (value && value.isAfter(this.to)) {
|
||||||
diff[options.prop] = value;
|
diff[options.prop] = value;
|
||||||
|
diff["to"] = value.clone().add(1, "hour");
|
||||||
const newState = Object.assign(state, diff);
|
|
||||||
|
|
||||||
const validation = this._valid(newState);
|
|
||||||
if (validation === true) {
|
|
||||||
this.set("error", null);
|
|
||||||
this.onChange(newState);
|
|
||||||
} else {
|
} else {
|
||||||
this.set("error", validation);
|
diff[options.prop] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
onChangePanel(panel) {
|
if (options.prop === "to") {
|
||||||
this.set("currentPanel", panel);
|
if (value && value.isBefore(this.from)) {
|
||||||
|
diff[options.prop] = this.from.clone().add(1, "hour");
|
||||||
|
} else {
|
||||||
|
diff[options.prop] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const newState = Object.assign({}, state, diff);
|
||||||
|
this.onChange(newState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,44 +1,63 @@
|
|||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import { computed } from "@ember/object";
|
import { computed, action } from "@ember/object";
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
classNames: ["d-date-time-input"],
|
classNames: ["d-date-time-input"],
|
||||||
date: null,
|
date: null,
|
||||||
|
relativeDate: null,
|
||||||
showTime: true,
|
showTime: true,
|
||||||
clearable: false,
|
clearable: false,
|
||||||
|
|
||||||
_hours: computed("date", function() {
|
hours: computed("date", "showTime", function() {
|
||||||
return this.date && this.showTime ? new Date(this.date).getHours() : null;
|
return this.date && this.get("showTime") ? this.date.hours() : null;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
_minutes: computed("date", function() {
|
minutes: computed("date", "showTime", function() {
|
||||||
return this.date && this.showTime ? new Date(this.date).getMinutes() : null;
|
return this.date && this.get("showTime") ? this.date.minutes() : null;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
actions: {
|
@action
|
||||||
onClear() {
|
onClear() {
|
||||||
this.onChange(null);
|
this.onChange(null);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@action
|
||||||
onChangeTime(time) {
|
onChangeTime(time) {
|
||||||
if (this.onChange) {
|
if (this.onChange) {
|
||||||
const date = new Date(this.date);
|
const date = this.date
|
||||||
const year = date.getFullYear();
|
? this.date
|
||||||
const month = date.getMonth();
|
: this.relativeDate
|
||||||
const day = date.getDate();
|
? this.relativeDate
|
||||||
this.onChange(new Date(year, month, day, time.hours, time.minutes));
|
: moment();
|
||||||
|
|
||||||
|
this.onChange(
|
||||||
|
moment({
|
||||||
|
year: date.year(),
|
||||||
|
month: date.month(),
|
||||||
|
day: date.date(),
|
||||||
|
hours: time.hours,
|
||||||
|
minutes: time.minutes
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@action
|
||||||
onChangeDate(date) {
|
onChangeDate(date) {
|
||||||
if (this.onChange) {
|
if (!date) {
|
||||||
const year = date.getFullYear();
|
this.onClear();
|
||||||
const month = date.getMonth();
|
return;
|
||||||
const day = date.getDate();
|
}
|
||||||
|
|
||||||
|
this.onChange &&
|
||||||
this.onChange(
|
this.onChange(
|
||||||
new Date(year, month, day, this._hours || 0, this._minutes || 0)
|
moment({
|
||||||
|
year: date.year(),
|
||||||
|
month: date.month(),
|
||||||
|
day: date.date(),
|
||||||
|
hours: this.hours || 0,
|
||||||
|
minutes: this.minutes || 0
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
@ -1,76 +1,171 @@
|
|||||||
import { oneWay, or } from "@ember/object/computed";
|
import { isPresent } from "@ember/utils";
|
||||||
import { schedule } from "@ember/runloop";
|
import { computed, action } from "@ember/object";
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import { isNumeric } from "discourse/lib/utilities";
|
|
||||||
|
function convertMinutes(num) {
|
||||||
|
return { hours: Math.floor(num / 60), minutes: num % 60 };
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertMinutesToString(n) {
|
||||||
|
const hoursAndMinutes = convertMinutes(n);
|
||||||
|
return `${hoursAndMinutes.hours
|
||||||
|
.toString()
|
||||||
|
.padStart(2, "0")}:${hoursAndMinutes.minutes.toString().padStart(2, "0")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertMinutesToDurationString(n) {
|
||||||
|
const hoursAndMinutes = convertMinutes(n);
|
||||||
|
|
||||||
|
let output = "";
|
||||||
|
|
||||||
|
if (hoursAndMinutes.hours) {
|
||||||
|
output = `${hoursAndMinutes.hours}h`;
|
||||||
|
|
||||||
|
if (hoursAndMinutes.minutes > 0) {
|
||||||
|
output = `${output} ${hoursAndMinutes.minutes} min`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
output = `${hoursAndMinutes.minutes} min`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
classNames: ["d-time-input"],
|
classNames: ["d-time-input"],
|
||||||
|
|
||||||
hours: null,
|
hours: null,
|
||||||
|
|
||||||
minutes: null,
|
minutes: null,
|
||||||
_hours: oneWay("hours"),
|
|
||||||
_minutes: oneWay("minutes"),
|
|
||||||
isSafari: oneWay("capabilities.isSafari"),
|
|
||||||
isMobile: oneWay("site.mobileView"),
|
|
||||||
nativePicker: or("isSafari", "isMobile"),
|
|
||||||
|
|
||||||
actions: {
|
relativeDate: null,
|
||||||
onInput(options, event) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
if (this.onChange) {
|
didReceiveAttrs() {
|
||||||
let value = event.target.value;
|
this._super(...arguments);
|
||||||
|
|
||||||
if (!isNumeric(value)) {
|
if (isPresent(this.date)) {
|
||||||
value = 0;
|
this.setProperties({
|
||||||
} else {
|
hours: this.date.hours(),
|
||||||
value = parseInt(value, 10);
|
minutes: this.date.minutes()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.prop === "hours") {
|
if (
|
||||||
value = Math.max(0, Math.min(value, 23))
|
!isPresent(this.date) &&
|
||||||
.toString()
|
!isPresent(this.attrs.hours) &&
|
||||||
.padStart(2, "0");
|
!isPresent(this.attrs.minutes)
|
||||||
this._processHoursChange(value);
|
) {
|
||||||
} else {
|
this.setProperties({
|
||||||
value = Math.max(0, Math.min(value, 59))
|
hours: null,
|
||||||
.toString()
|
minutes: null
|
||||||
.padStart(2, "0");
|
});
|
||||||
this._processMinutesChange(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
schedule("afterRender", () => (event.target.value = value));
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
minimumTime: computed("relativeDate", "date", function() {
|
||||||
|
if (this.relativeDate) {
|
||||||
|
if (this.date) {
|
||||||
|
if (this.date.diff(this.relativeDate, "minutes") > 1440) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return this.relativeDate.hours() * 60 + this.relativeDate.minutes();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return this.relativeDate.hours() * 60 + this.relativeDate.minutes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
timeOptions: computed("minimumTime", "hours", "minutes", function() {
|
||||||
|
let options = [];
|
||||||
|
|
||||||
|
const start = this.minimumTime
|
||||||
|
? this.minimumTime > this.time
|
||||||
|
? this.time
|
||||||
|
: this.minimumTime
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
// theres 1440 minutes in a day
|
||||||
|
// and 1440 / 15 = 96
|
||||||
|
let i = 0;
|
||||||
|
while (i < 96) {
|
||||||
|
// while diff with minimumTime is less than one hour
|
||||||
|
// use 15 minutes steps and then 30 minutes
|
||||||
|
const minutes = this.minimumTime ? (i <= 4 ? 15 : 30) : 15;
|
||||||
|
const option = start + i * minutes;
|
||||||
|
|
||||||
|
// when start is higher than 0 we will reach 1440 minutes
|
||||||
|
// before the 96 iterations
|
||||||
|
if (option > 1440) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
options.push(option);
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.time && !options.includes(this.time)) {
|
||||||
|
options = [this.time].concat(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
options = options.sort((a, b) => a - b);
|
||||||
|
|
||||||
|
return options.map(option => {
|
||||||
|
let name = convertMinutesToString(option);
|
||||||
|
let label;
|
||||||
|
|
||||||
|
if (this.minimumTime) {
|
||||||
|
const diff = option - this.minimumTime;
|
||||||
|
label = `${name} <small>(${convertMinutesToDurationString(
|
||||||
|
diff
|
||||||
|
)})</small>`.htmlSafe();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: option,
|
||||||
|
name,
|
||||||
|
label,
|
||||||
|
title: name
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
|
||||||
|
time: computed("minimumTime", "hours", "minutes", function() {
|
||||||
|
if (isPresent(this.hours) && isPresent(this.minutes)) {
|
||||||
|
return parseInt(this.hours, 10) * 60 + parseInt(this.minutes, 10);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
@action
|
||||||
onFocusIn(value, event) {
|
onFocusIn(value, event) {
|
||||||
if (value && event.target) {
|
if (value && event.target) {
|
||||||
event.target.select();
|
event.target.select();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onChangeTime(event) {
|
@action
|
||||||
const time = event.target.value;
|
onChangeTime(time) {
|
||||||
|
if (isPresent(time) && this.onChange) {
|
||||||
|
if (typeof time === "string" && time.length) {
|
||||||
|
let [hours, minutes] = time.split(":");
|
||||||
|
if (hours && minutes) {
|
||||||
|
if (hours < 0) hours = 0;
|
||||||
|
if (hours > 23) hours = 23;
|
||||||
|
if (minutes < 0) minutes = 0;
|
||||||
|
if (minutes > 59) minutes = 59;
|
||||||
|
|
||||||
if (time && this.onChange) {
|
|
||||||
this.onChange({
|
this.onChange({
|
||||||
hours: time.split(":")[0],
|
hours: parseInt(hours, 10),
|
||||||
minutes: time.split(":")[1]
|
minutes: parseInt(minutes, 10)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.onChange({
|
||||||
|
hours: convertMinutes(time).hours,
|
||||||
|
minutes: convertMinutes(time).minutes
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
_processHoursChange(hours) {
|
|
||||||
this.onChange({
|
|
||||||
hours,
|
|
||||||
minutes: this._minutes || "00"
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_processMinutesChange(minutes) {
|
|
||||||
this.onChange({
|
|
||||||
hours: this._hours || "00",
|
|
||||||
minutes
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
import Controller from "@ember/controller";
|
import Controller from "@ember/controller";
|
||||||
|
import { isPresent } from "@ember/utils";
|
||||||
|
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
queryParams: [
|
queryParams: [
|
||||||
@ -86,12 +87,7 @@ export default Controller.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
setRange(range) {
|
setRange(range) {
|
||||||
if (range.from) {
|
this.setProperties(range);
|
||||||
this.set("from", new Date(range.from).toISOString().split("T")[0]);
|
|
||||||
}
|
|
||||||
if (range.to) {
|
|
||||||
this.set("to", new Date(range.to).toISOString().split("T")[0]);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
@ -118,8 +114,12 @@ export default Controller.extend({
|
|||||||
status: this.filterStatus,
|
status: this.filterStatus,
|
||||||
category_id: this.filterCategoryId,
|
category_id: this.filterCategoryId,
|
||||||
username: this.filterUsername,
|
username: this.filterUsername,
|
||||||
from_date: this.filterFromDate,
|
from_date: isPresent(this.filterFromDate)
|
||||||
to_date: this.filterToDate,
|
? this.filterFromDate.toISOString(true).split("T")[0]
|
||||||
|
: null,
|
||||||
|
to_date: isPresent(this.filterToDate)
|
||||||
|
? this.filterToDate.toISOString(true).split("T")[0]
|
||||||
|
: null,
|
||||||
sort_order: this.filterSortOrder,
|
sort_order: this.filterSortOrder,
|
||||||
additional_filters: JSON.stringify(this.additionalFilters)
|
additional_filters: JSON.stringify(this.additionalFilters)
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import DiscourseRoute from "discourse/routes/discourse";
|
import DiscourseRoute from "discourse/routes/discourse";
|
||||||
|
import { isPresent } from "@ember/utils";
|
||||||
|
|
||||||
export default DiscourseRoute.extend({
|
export default DiscourseRoute.extend({
|
||||||
model(params) {
|
model(params) {
|
||||||
@ -23,8 +24,8 @@ export default DiscourseRoute.extend({
|
|||||||
filterPriority: meta.priority,
|
filterPriority: meta.priority,
|
||||||
reviewableTypes: meta.reviewable_types,
|
reviewableTypes: meta.reviewable_types,
|
||||||
filterUsername: meta.username,
|
filterUsername: meta.username,
|
||||||
filterFromDate: meta.from_date,
|
filterFromDate: isPresent(meta.from_date) ? moment(meta.from_date) : null,
|
||||||
filterToDate: meta.to_date,
|
filterToDate: isPresent(meta.to_date) ? moment(meta.to_date) : null,
|
||||||
filterSortOrder: meta.sort_order,
|
filterSortOrder: meta.sort_order,
|
||||||
additionalFilters: meta.additional_filters || {}
|
additionalFilters: meta.additional_filters || {}
|
||||||
});
|
});
|
||||||
|
@ -2,9 +2,8 @@
|
|||||||
type=inputType
|
type=inputType
|
||||||
class="date-picker"
|
class="date-picker"
|
||||||
placeholder=placeholder
|
placeholder=placeholder
|
||||||
value=value
|
value=(readonly value)
|
||||||
input=(action "onInput")
|
input=(action "onChangeDate")
|
||||||
readonly=true
|
|
||||||
}}
|
}}
|
||||||
|
|
||||||
<div class="picker-container"></div>
|
<div class="picker-container"></div>
|
||||||
|
@ -1,35 +1,16 @@
|
|||||||
<ul class="panels {{currentPanel}}">
|
|
||||||
<li>
|
|
||||||
{{d-button
|
|
||||||
label="date_time_picker.from"
|
|
||||||
class="from-panel"
|
|
||||||
action=(action "onChangePanel" "from")}}
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
{{d-button
|
|
||||||
label="date_time_picker.to"
|
|
||||||
class="to-panel"
|
|
||||||
action=(action "onChangePanel" "to")}}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
{{#if error}}
|
|
||||||
<div class="alert error">{{error}}</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<div class="panel from {{if fromPanelActive "visible"}}">
|
|
||||||
{{date-time-input
|
{{date-time-input
|
||||||
date=from
|
date=from
|
||||||
onChange=(action "_onChange" (hash prop="from"))
|
onChange=(action "onChangeRanges" (hash prop="from"))
|
||||||
showTime=showFromTime
|
showTime=showFromTime
|
||||||
|
class="from"
|
||||||
}}
|
}}
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel to {{if toPanelActive "visible"}}">
|
|
||||||
{{date-time-input
|
{{date-time-input
|
||||||
date=to
|
date=to
|
||||||
onChange=(action "_onChange" (hash prop="to"))
|
relativeDate=from
|
||||||
|
onChange=(action "onChangeRanges" (hash prop="to"))
|
||||||
|
timeFirst=toTimeFirst
|
||||||
showTime=showToTime
|
showTime=showToTime
|
||||||
clearable=true
|
clearable=clearable
|
||||||
|
class="to"
|
||||||
}}
|
}}
|
||||||
</div>
|
|
||||||
|
@ -1,13 +1,31 @@
|
|||||||
{{date-input date=date onChange=(action "onChangeDate")}}
|
{{#unless timeFirst}}
|
||||||
|
{{date-input
|
||||||
|
date=date
|
||||||
|
relativeDate=relativeDate
|
||||||
|
onChange=(action "onChangeDate")
|
||||||
|
}}
|
||||||
|
{{/unless}}
|
||||||
|
|
||||||
{{#if showTime}}
|
{{#if showTime}}
|
||||||
{{time-input
|
{{time-input
|
||||||
hours=_hours
|
date=date
|
||||||
minutes=_minutes
|
relativeDate=relativeDate
|
||||||
onChange=(action "onChangeTime")
|
onChange=(action "onChangeTime")
|
||||||
}}
|
}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if timeFirst}}
|
||||||
|
{{date-input
|
||||||
|
date=date
|
||||||
|
relativeDate=relativeDate
|
||||||
|
onChange=(action "onChangeDate")
|
||||||
|
}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
{{#if clearable}}
|
{{#if clearable}}
|
||||||
{{d-button icon="times" action=(action "onClear")}}
|
{{d-button
|
||||||
{{/if}}
|
class="clear-date-time"
|
||||||
|
icon="times"
|
||||||
|
action=(action "onClear")
|
||||||
|
}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -1,40 +1,12 @@
|
|||||||
<div class="fields">
|
{{combo-box
|
||||||
{{#if nativePicker}}
|
value=time
|
||||||
{{input
|
content=timeOptions
|
||||||
class="field time"
|
onChange=(action "onChangeTime")
|
||||||
type="time"
|
options=(hash
|
||||||
value=(concat _hours ":" _minutes)
|
translatedNone="--:--"
|
||||||
change=(action "onChangeTime")
|
allowAny=true
|
||||||
|
filterable=false
|
||||||
|
autoInsertNoneItem=false
|
||||||
|
translatedFilterPlaceholder="--:--"
|
||||||
|
)
|
||||||
}}
|
}}
|
||||||
{{else}}
|
|
||||||
{{input
|
|
||||||
class="field hours"
|
|
||||||
type="number"
|
|
||||||
title="Hours"
|
|
||||||
minlength=2
|
|
||||||
maxlength=2
|
|
||||||
max="23"
|
|
||||||
min="0"
|
|
||||||
placeholder="00"
|
|
||||||
value=_hours
|
|
||||||
input=(action "onInput" (hash prop="hours"))
|
|
||||||
focus-in=(action "onFocusIn")
|
|
||||||
}}
|
|
||||||
|
|
||||||
<div class="separator">:</div>
|
|
||||||
|
|
||||||
{{input
|
|
||||||
class="field minutes"
|
|
||||||
title="Minutes"
|
|
||||||
type="number"
|
|
||||||
minlength=2
|
|
||||||
maxlength=2
|
|
||||||
max="59"
|
|
||||||
min="0"
|
|
||||||
placeholder="00"
|
|
||||||
value=_minutes
|
|
||||||
input=(action "onInput" (hash prop="minutes"))
|
|
||||||
focus-in=(action "onFocusIn")
|
|
||||||
}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
|
@ -77,7 +77,13 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<div class="reviewable-filter date-range">
|
<div class="reviewable-filter date-range">
|
||||||
{{date-time-input-range showFromTime=false showToTime=false from=filterFromDate to=filterToDate onChange=setRange}}
|
{{date-time-input-range
|
||||||
|
from=filterFromDate
|
||||||
|
to=filterToDate
|
||||||
|
onChange=setRange
|
||||||
|
showFromTime=false
|
||||||
|
showToTime=false
|
||||||
|
}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="reviewable-filter sort-order">
|
<div class="reviewable-filter sort-order">
|
||||||
|
@ -482,10 +482,7 @@ export default Component.extend(
|
|||||||
this.selectKit.options.allowAny &&
|
this.selectKit.options.allowAny &&
|
||||||
!this.selectKit.isExpanded
|
!this.selectKit.isExpanded
|
||||||
) {
|
) {
|
||||||
return this.defaultItem(
|
return null;
|
||||||
null,
|
|
||||||
I18n.t("select_kit.filter_placeholder_with_any")
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let item;
|
let item;
|
||||||
@ -754,8 +751,6 @@ export default Component.extend(
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onCloseWrapper(event) {
|
_onCloseWrapper(event) {
|
||||||
this._focusFilter(this.multiSelect);
|
|
||||||
|
|
||||||
this.set("selectKit.highlighted", null);
|
this.set("selectKit.highlighted", null);
|
||||||
|
|
||||||
let boundaryAction = this._boundaryActionHandler("onClose");
|
let boundaryAction = this._boundaryActionHandler("onClose");
|
||||||
|
@ -161,13 +161,9 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.date-picker-wrapper {
|
.d-date-time-input-range {
|
||||||
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
.date-picker {
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,6 +126,7 @@
|
|||||||
width: inherit;
|
width: inherit;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
.d-date-input {
|
.d-date-input {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
|
@ -116,11 +116,9 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
#search-min-post-count,
|
#search-min-post-count,
|
||||||
.date-picker,
|
|
||||||
.combo-box,
|
.combo-box,
|
||||||
.ac-wrap,
|
.ac-wrap,
|
||||||
.control-group,
|
.control-group,
|
||||||
.date-picker-wrapper,
|
|
||||||
.search-advanced-category-chooser {
|
.search-advanced-category-chooser {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -131,15 +129,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.date-picker-wrapper {
|
.d-date-input {
|
||||||
margin-top: 0.5em;
|
margin-top: 0.5em;
|
||||||
}
|
width: 100%;
|
||||||
|
|
||||||
.date-picker {
|
|
||||||
box-sizing: border-box;
|
|
||||||
text-align: left;
|
|
||||||
padding: 4px;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-advanced-title {
|
.search-advanced-title {
|
||||||
|
@ -1,19 +1,58 @@
|
|||||||
.d-date-input {
|
.d-date-input {
|
||||||
display: flex;
|
display: inline-flex;
|
||||||
flex: 1;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
min-width: 140px;
|
||||||
|
|
||||||
.date-picker {
|
.date-picker {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
outline: none;
|
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
|
|
||||||
|
&::-webkit-input-placeholder {
|
||||||
|
font-size: $font-0;
|
||||||
|
color: $primary-medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-ms-input-placeholder {
|
||||||
|
font-size: $font-0;
|
||||||
|
color: $primary-medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
font-size: $font-0;
|
||||||
|
color: $primary-medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: 1px solid $tertiary;
|
||||||
|
outline-offset: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.pika-single {
|
.pika-single {
|
||||||
margin-left: -1px;
|
margin-left: -1px;
|
||||||
|
margin-top: 1px;
|
||||||
|
|
||||||
|
.pika-row td {
|
||||||
|
.pika-button.pika-day {
|
||||||
|
box-shadow: none;
|
||||||
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-date-input + .d-time-input {
|
||||||
|
margin-left: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-time-input + .d-date-input {
|
||||||
|
margin-left: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-date-input + .clear-date-time {
|
||||||
|
margin-left: 1px;
|
||||||
|
}
|
||||||
|
@ -1,41 +1,5 @@
|
|||||||
.d-date-time-input-range {
|
.d-date-time-input-range {
|
||||||
padding: 0.5em;
|
|
||||||
border: 1px solid $primary-low;
|
border: 1px solid $primary-low;
|
||||||
width: 300px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.panels {
|
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
list-style: none;
|
box-sizing: border-box;
|
||||||
margin: 0 0 0.5em 0;
|
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
&.from {
|
|
||||||
.from-panel {
|
|
||||||
background: $danger;
|
|
||||||
color: $secondary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.to {
|
|
||||||
.to-panel {
|
|
||||||
background: $danger;
|
|
||||||
color: $secondary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
margin-right: 0.5em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel {
|
|
||||||
display: none;
|
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
&.visible {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
.d-date-time-input {
|
.d-date-time-input {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
|
||||||
border: 1px solid $primary-low;
|
border: 1px solid $primary-low;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
position: relative;
|
position: relative;
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
.date-picker,
|
.date-picker,
|
||||||
.fields {
|
.fields {
|
||||||
@ -13,5 +11,22 @@
|
|||||||
|
|
||||||
.d-date-time-input {
|
.d-date-time-input {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-time-input {
|
||||||
|
.select-kit.combo-box {
|
||||||
|
.select-kit-header {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-date-input + .d-time-input {
|
||||||
|
margin-left: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-time-input + .d-date-input {
|
||||||
|
margin-right: 1px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,39 +1,43 @@
|
|||||||
|
.modal-inner-container .d-time-input,
|
||||||
.d-time-input {
|
.d-time-input {
|
||||||
box-sizing: border-box;
|
display: inline-flex;
|
||||||
|
|
||||||
.fields {
|
.combo-box {
|
||||||
display: flex;
|
width: 65px;
|
||||||
align-items: center;
|
min-width: auto;
|
||||||
border: 1px solid $primary-low;
|
|
||||||
|
|
||||||
.field {
|
&:not(.has-selection) {
|
||||||
text-align: center;
|
.select-kit-selected-name .name {
|
||||||
|
color: $primary-medium;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-kit-header {
|
||||||
|
.d-icon {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: 1px solid $tertiary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-kit-collection {
|
||||||
|
min-width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-kit-filter {
|
||||||
width: auto;
|
width: auto;
|
||||||
margin: 0;
|
overflow: hidden;
|
||||||
border: none;
|
.filter-input {
|
||||||
outline: none;
|
min-width: 0;
|
||||||
box-shadow: none;
|
}
|
||||||
width: 32px;
|
|
||||||
|
|
||||||
&.time {
|
|
||||||
width: 100%;
|
|
||||||
text-align: left;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.hours,
|
.selected-name {
|
||||||
&.minutes {
|
white-space: nowrap;
|
||||||
text-align: center;
|
overflow: hidden;
|
||||||
width: 45px;
|
text-overflow: ellipsis;
|
||||||
}
|
|
||||||
|
|
||||||
&.hours {
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.minutes {
|
|
||||||
padding-left: 10px;
|
|
||||||
width: 55px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -304,7 +304,6 @@ en:
|
|||||||
unbookmark: "Click to remove all bookmarks in this topic"
|
unbookmark: "Click to remove all bookmarks in this topic"
|
||||||
unbookmark_with_reminder: "Click to remove all bookmarks and reminders in this topic. You have a reminder set %{reminder_at} for this topic."
|
unbookmark_with_reminder: "Click to remove all bookmarks and reminders in this topic. You have a reminder set %{reminder_at} for this topic."
|
||||||
|
|
||||||
|
|
||||||
bookmarks:
|
bookmarks:
|
||||||
created: "you've bookmarked this post"
|
created: "you've bookmarked this post"
|
||||||
not_bookmarked: "bookmark this post"
|
not_bookmarked: "bookmark this post"
|
||||||
@ -3393,8 +3392,7 @@ en:
|
|||||||
view_table: "table"
|
view_table: "table"
|
||||||
view_graph: "graph"
|
view_graph: "graph"
|
||||||
refresh_report: "Refresh Report"
|
refresh_report: "Refresh Report"
|
||||||
start_date: "Start Date (UTC)"
|
dates: "Dates (UTC)"
|
||||||
end_date: "End Date (UTC)"
|
|
||||||
groups: "All groups"
|
groups: "All groups"
|
||||||
disabled: "This report is disabled"
|
disabled: "This report is disabled"
|
||||||
totals_for_sample: "Totals for sample"
|
totals_for_sample: "Totals for sample"
|
||||||
|
@ -102,7 +102,7 @@ QUnit.test("reports tab", async assert => {
|
|||||||
|
|
||||||
QUnit.test("report filters", async assert => {
|
QUnit.test("report filters", async assert => {
|
||||||
await visit(
|
await visit(
|
||||||
'/admin/reports/signups?end_date=2018-07-16&filters=%7B"group"%3A88%7D&start_date=2018-06-16'
|
'/admin/reports/signups_with_groups?end_date=2018-07-16&filters=%7B"group"%3A88%7D&start_date=2018-06-16'
|
||||||
);
|
);
|
||||||
|
|
||||||
const groupFilter = selectKit(".group-filter .combo-box");
|
const groupFilter = selectKit(".group-filter .combo-box");
|
||||||
|
@ -18,7 +18,7 @@ async function pika(year, month, day) {
|
|||||||
|
|
||||||
function noop() {}
|
function noop() {}
|
||||||
|
|
||||||
const DEFAULT_DATE = new Date(2019, 0, 29);
|
const DEFAULT_DATE = moment("2019-01-29");
|
||||||
|
|
||||||
componentTest("default", {
|
componentTest("default", {
|
||||||
template: `{{date-input date=date}}`,
|
template: `{{date-input date=date}}`,
|
||||||
@ -44,7 +44,7 @@ componentTest("prevents mutations", {
|
|||||||
await click(dateInput());
|
await click(dateInput());
|
||||||
await pika(2019, 0, 2);
|
await pika(2019, 0, 2);
|
||||||
|
|
||||||
assert.ok(this.date.getTime() === DEFAULT_DATE.getTime());
|
assert.ok(this.date.isSame(DEFAULT_DATE));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -60,6 +60,6 @@ componentTest("allows mutations through actions", {
|
|||||||
await click(dateInput());
|
await click(dateInput());
|
||||||
await pika(2019, 0, 2);
|
await pika(2019, 0, 2);
|
||||||
|
|
||||||
assert.ok(this.date.getTime() === new Date(2019, 0, 2).getTime());
|
assert.ok(this.date.isSame(moment("2019-01-02")));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -3,40 +3,22 @@ import componentTest from "helpers/component-test";
|
|||||||
moduleForComponent("date-time-input-range", { integration: true });
|
moduleForComponent("date-time-input-range", { integration: true });
|
||||||
|
|
||||||
function fromDateInput() {
|
function fromDateInput() {
|
||||||
return find(".from .date-picker");
|
return find(".from.d-date-time-input .date-picker")[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
function fromHoursInput() {
|
function fromTimeInput() {
|
||||||
return find(".from .field.hours");
|
return find(".from.d-date-time-input .d-time-input .combo-box-header")[0];
|
||||||
}
|
|
||||||
|
|
||||||
function fromMinutesInput() {
|
|
||||||
return find(".from .field.minutes");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function toDateInput() {
|
function toDateInput() {
|
||||||
return find(".to .date-picker");
|
return find(".to.d-date-time-input .date-picker")[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
function toHoursInput() {
|
function toTimeInput() {
|
||||||
return find(".to .field.hours");
|
return find(".to.d-date-time-input .d-time-input .combo-box-header")[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
function toMinutesInput() {
|
const DEFAULT_DATE_TIME = moment("2019-01-29 14:45");
|
||||||
return find(".to .field.minutes");
|
|
||||||
}
|
|
||||||
|
|
||||||
function setDates(dates) {
|
|
||||||
this.setProperties(dates);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function pika(year, month, day) {
|
|
||||||
await click(
|
|
||||||
`.pika-button.pika-day[data-pika-year="${year}"][data-pika-month="${month}"][data-pika-day="${day}"]`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const DEFAULT_DATE_TIME = new Date(2019, 0, 29, 14, 45);
|
|
||||||
|
|
||||||
componentTest("default", {
|
componentTest("default", {
|
||||||
template: `{{date-time-input-range from=from to=to}}`,
|
template: `{{date-time-input-range from=from to=to}}`,
|
||||||
@ -46,60 +28,9 @@ componentTest("default", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
test(assert) {
|
test(assert) {
|
||||||
assert.equal(fromDateInput().val(), "January 29, 2019");
|
assert.equal(fromDateInput().value, "January 29, 2019");
|
||||||
assert.equal(fromHoursInput().val(), "14");
|
assert.equal(fromTimeInput().dataset.name, "14:45");
|
||||||
assert.equal(fromMinutesInput().val(), "45");
|
assert.equal(toDateInput().value, "");
|
||||||
|
assert.equal(toTimeInput().dataset.name, "--:--");
|
||||||
assert.equal(toDateInput().val(), "");
|
|
||||||
assert.equal(toHoursInput().val(), "");
|
|
||||||
assert.equal(toMinutesInput().val(), "");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
componentTest("can switch panels", {
|
|
||||||
template: `{{date-time-input-range}}`,
|
|
||||||
|
|
||||||
async test(assert) {
|
|
||||||
assert.ok(exists(".panel.from.visible"));
|
|
||||||
assert.notOk(exists(".panel.to.visible"));
|
|
||||||
|
|
||||||
await click(".panels button.to-panel");
|
|
||||||
|
|
||||||
assert.ok(exists(".panel.to.visible"));
|
|
||||||
assert.notOk(exists(".panel.from.visible"));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
componentTest("prevents toDate to be before fromDate", {
|
|
||||||
template: `{{date-time-input-range from=from to=to onChange=onChange}}`,
|
|
||||||
|
|
||||||
beforeEach() {
|
|
||||||
this.setProperties({
|
|
||||||
from: DEFAULT_DATE_TIME,
|
|
||||||
to: DEFAULT_DATE_TIME,
|
|
||||||
onChange: setDates
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
async test(assert) {
|
|
||||||
assert.notOk(exists(".error"), "it begins with no error");
|
|
||||||
|
|
||||||
await click(".panels button.to-panel");
|
|
||||||
await click(toDateInput());
|
|
||||||
await pika(2019, 0, 1);
|
|
||||||
|
|
||||||
assert.ok(exists(".error"), "it shows an error");
|
|
||||||
assert.deepEqual(this.to, DEFAULT_DATE_TIME, "it didnt trigger a mutation");
|
|
||||||
|
|
||||||
await click(".panels button.to-panel");
|
|
||||||
await click(toDateInput());
|
|
||||||
await pika(2019, 0, 30);
|
|
||||||
|
|
||||||
assert.notOk(exists(".error"), "it removes the error");
|
|
||||||
assert.deepEqual(
|
|
||||||
this.to,
|
|
||||||
new Date(2019, 0, 30, 14, 45),
|
|
||||||
"it has changed the date"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -3,15 +3,11 @@ import componentTest from "helpers/component-test";
|
|||||||
moduleForComponent("date-time-input", { integration: true });
|
moduleForComponent("date-time-input", { integration: true });
|
||||||
|
|
||||||
function dateInput() {
|
function dateInput() {
|
||||||
return find(".date-picker");
|
return find(".date-picker")[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
function hoursInput() {
|
function timeInput() {
|
||||||
return find(".field.hours");
|
return find(".d-time-input .combo-box-header")[0];
|
||||||
}
|
|
||||||
|
|
||||||
function minutesInput() {
|
|
||||||
return find(".field.minutes");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setDate(date) {
|
function setDate(date) {
|
||||||
@ -24,7 +20,7 @@ async function pika(year, month, day) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_DATE_TIME = new Date(2019, 0, 29, 14, 45);
|
const DEFAULT_DATE_TIME = moment("2019-01-29 14:45");
|
||||||
|
|
||||||
componentTest("default", {
|
componentTest("default", {
|
||||||
template: `{{date-time-input date=date}}`,
|
template: `{{date-time-input date=date}}`,
|
||||||
@ -34,9 +30,8 @@ componentTest("default", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
test(assert) {
|
test(assert) {
|
||||||
assert.equal(dateInput().val(), "January 29, 2019");
|
assert.equal(dateInput().value, "January 29, 2019");
|
||||||
assert.equal(hoursInput().val(), "14");
|
assert.equal(timeInput().dataset.name, "14:45");
|
||||||
assert.equal(minutesInput().val(), "45");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -51,7 +46,7 @@ componentTest("prevents mutations", {
|
|||||||
await click(dateInput());
|
await click(dateInput());
|
||||||
await pika(2019, 0, 2);
|
await pika(2019, 0, 2);
|
||||||
|
|
||||||
assert.ok(this.date.getTime() === DEFAULT_DATE_TIME.getTime());
|
assert.ok(this.date.isSame(DEFAULT_DATE_TIME));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -67,7 +62,7 @@ componentTest("allows mutations through actions", {
|
|||||||
await click(dateInput());
|
await click(dateInput());
|
||||||
await pika(2019, 0, 2);
|
await pika(2019, 0, 2);
|
||||||
|
|
||||||
assert.ok(this.date.getTime() === new Date(2019, 0, 2, 14, 45).getTime());
|
assert.ok(this.date.isSame(moment("2019-01-02 14:45")));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -79,6 +74,6 @@ componentTest("can hide time", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async test(assert) {
|
async test(assert) {
|
||||||
assert.notOk(exists(hoursInput()));
|
assert.notOk(exists(timeInput()));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,21 +1,18 @@
|
|||||||
|
import selectKit from "helpers/select-kit-helper";
|
||||||
import componentTest from "helpers/component-test";
|
import componentTest from "helpers/component-test";
|
||||||
|
|
||||||
moduleForComponent("time-input", { integration: true });
|
moduleForComponent("time-input", {
|
||||||
|
integration: true,
|
||||||
|
|
||||||
function hoursInput() {
|
beforeEach() {
|
||||||
return find(".field.hours");
|
this.set("subject", selectKit());
|
||||||
}
|
|
||||||
|
|
||||||
function minutesInput() {
|
|
||||||
return find(".field.minutes");
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
function setTime(time) {
|
function setTime(time) {
|
||||||
this.setProperties(time);
|
this.setProperties(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
function noop() {}
|
|
||||||
|
|
||||||
componentTest("default", {
|
componentTest("default", {
|
||||||
template: `{{time-input hours=hours minutes=minutes}}`,
|
template: `{{time-input hours=hours minutes=minutes}}`,
|
||||||
|
|
||||||
@ -24,8 +21,7 @@ componentTest("default", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
test(assert) {
|
test(assert) {
|
||||||
assert.equal(hoursInput().val(), "14");
|
assert.equal(this.subject.header().name(), "14:58");
|
||||||
assert.equal(minutesInput().val(), "58");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -37,11 +33,9 @@ componentTest("prevents mutations", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async test(assert) {
|
async test(assert) {
|
||||||
await fillIn(hoursInput(), "12");
|
await this.subject.expand();
|
||||||
assert.ok(this.hours === "14");
|
await this.subject.selectRowByIndex(3);
|
||||||
|
assert.equal(this.subject.header().name(), "14:58");
|
||||||
await fillIn(minutesInput(), "36");
|
|
||||||
assert.ok(this.minutes === "58");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -54,44 +48,8 @@ componentTest("allows mutations through actions", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async test(assert) {
|
async test(assert) {
|
||||||
await fillIn(hoursInput(), "12");
|
await this.subject.expand();
|
||||||
assert.ok(this.hours === "12");
|
await this.subject.selectRowByIndex(3);
|
||||||
|
assert.equal(this.subject.header().name(), "00:45");
|
||||||
await fillIn(minutesInput(), "36");
|
|
||||||
assert.ok(this.minutes === "36");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
componentTest("hours and minutes have boundaries", {
|
|
||||||
template: `{{time-input hours=14 minutes=58 onChange=onChange}}`,
|
|
||||||
|
|
||||||
beforeEach() {
|
|
||||||
this.set("onChange", noop);
|
|
||||||
},
|
|
||||||
|
|
||||||
async test(assert) {
|
|
||||||
await fillIn(hoursInput(), "2");
|
|
||||||
assert.equal(hoursInput().val(), "02");
|
|
||||||
|
|
||||||
await fillIn(hoursInput(), "@");
|
|
||||||
assert.equal(hoursInput().val(), "00");
|
|
||||||
|
|
||||||
await fillIn(hoursInput(), "24");
|
|
||||||
assert.equal(hoursInput().val(), "23");
|
|
||||||
|
|
||||||
await fillIn(hoursInput(), "-1");
|
|
||||||
assert.equal(hoursInput().val(), "00");
|
|
||||||
|
|
||||||
await fillIn(minutesInput(), "@");
|
|
||||||
assert.equal(minutesInput().val(), "00");
|
|
||||||
|
|
||||||
await fillIn(minutesInput(), "2");
|
|
||||||
assert.equal(minutesInput().val(), "02");
|
|
||||||
|
|
||||||
await fillIn(minutesInput(), "60");
|
|
||||||
assert.equal(minutesInput().val(), "59");
|
|
||||||
|
|
||||||
await fillIn(minutesInput(), "-1");
|
|
||||||
assert.equal(minutesInput().val(), "00");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -59,7 +59,7 @@ let signups = {
|
|||||||
prev_end_date: "2018-06-17T00:00:00Z",
|
prev_end_date: "2018-06-17T00:00:00Z",
|
||||||
prev30Days: null,
|
prev30Days: null,
|
||||||
dates_filtering: true,
|
dates_filtering: true,
|
||||||
report_key: 'reports:signups:start:end:[:prev_period]:50:{"group":"88"}:4',
|
report_key: "reports:signups:start:end:[:prev_period]:4",
|
||||||
available_filters: [
|
available_filters: [
|
||||||
{ id: "group", type: "group", allow_any: false, choices: [], default: "88" }
|
{ id: "group", type: "group", allow_any: false, choices: [], default: "88" }
|
||||||
],
|
],
|
||||||
@ -77,18 +77,29 @@ let signups = {
|
|||||||
let signups_fixture = JSON.parse(JSON.stringify(signups));
|
let signups_fixture = JSON.parse(JSON.stringify(signups));
|
||||||
signups_fixture.type = "signups_exception";
|
signups_fixture.type = "signups_exception";
|
||||||
signups_fixture.error = "exception";
|
signups_fixture.error = "exception";
|
||||||
|
signups_fixture.report_key =
|
||||||
|
"reports:signups_exception:start:end:[:prev_period]:4";
|
||||||
const signups_exception = signups_fixture;
|
const signups_exception = signups_fixture;
|
||||||
|
|
||||||
signups_fixture = JSON.parse(JSON.stringify(signups));
|
signups_fixture = JSON.parse(JSON.stringify(signups));
|
||||||
signups_fixture.type = "signups_timeout";
|
signups_fixture.type = "signups_timeout";
|
||||||
signups_fixture.error = "timeout";
|
signups_fixture.error = "timeout";
|
||||||
|
signups_fixture.report_key =
|
||||||
|
"reports:signups_timeout:start:end:[:prev_period]:4";
|
||||||
const signups_timeout = signups_fixture;
|
const signups_timeout = signups_fixture;
|
||||||
|
|
||||||
signups_fixture = JSON.parse(JSON.stringify(signups));
|
signups_fixture = JSON.parse(JSON.stringify(signups));
|
||||||
signups_fixture.type = "not_found";
|
signups_fixture.type = "not_found";
|
||||||
signups_fixture.error = "not_found";
|
signups_fixture.error = "not_found";
|
||||||
|
signups_fixture.report_key = "reports:not_found:start:end:[:prev_period]:4";
|
||||||
const signups_not_found = signups_fixture;
|
const signups_not_found = signups_fixture;
|
||||||
|
|
||||||
|
signups_fixture = JSON.parse(JSON.stringify(signups));
|
||||||
|
signups_fixture.type = "signups_with_groups";
|
||||||
|
signups_fixture.report_key =
|
||||||
|
'reports:signups_with_groups:start:end:[:prev_period]:50:{"group":"88"}:4';
|
||||||
|
const signups_with_group = signups_fixture;
|
||||||
|
|
||||||
const startDate = moment()
|
const startDate = moment()
|
||||||
.locale("en")
|
.locale("en")
|
||||||
.utc()
|
.utc()
|
||||||
@ -180,6 +191,7 @@ export default {
|
|||||||
"/admin/reports/bulk": {
|
"/admin/reports/bulk": {
|
||||||
reports: [
|
reports: [
|
||||||
signups,
|
signups,
|
||||||
|
signups_with_group,
|
||||||
signups_not_found,
|
signups_not_found,
|
||||||
signups_exception,
|
signups_exception,
|
||||||
signups_timeout,
|
signups_timeout,
|
||||||
|
Reference in New Issue
Block a user