Implement query zoom handling in frontend

Had to refactor:

1. The zoom level enum in the frontend cannot properly be matched to
strings so I chose a separate type instead to map to the string
retrieved from /sent to the query

2. The way query updates are done through the states. The previous
implementation only worked for one single attribute state.

For the timeline state, it does not make sense to split that into two,
so I delegate to the state to handle checking and updating the query.
pull/5607/head
Oliver Günther 8 years ago committed by Jens Ulferts
parent 6b372acae6
commit d6b8b92037
No known key found for this signature in database
GPG Key ID: 3CAA4B1182CF5308
  1. 3
      frontend/app/components/api/api-v3/hal-resources/query-resource.service.ts
  2. 34
      frontend/app/components/routing/wp-list/wp-list.controller.ts
  3. 7
      frontend/app/components/wp-buttons/wp-timeline-toggle-button/wp-timeline-toggle-button.directive.ts
  4. 12
      frontend/app/components/wp-fast-table/state/wp-table-timeline.service.ts
  5. 24
      frontend/app/components/wp-fast-table/wp-table-base.ts
  6. 12
      frontend/app/components/wp-fast-table/wp-table-columns.ts
  7. 16
      frontend/app/components/wp-fast-table/wp-table-filters.ts
  8. 16
      frontend/app/components/wp-fast-table/wp-table-group-by.ts
  9. 13
      frontend/app/components/wp-fast-table/wp-table-hierarchies.ts
  10. 1
      frontend/app/components/wp-fast-table/wp-table-pagination.ts
  11. 12
      frontend/app/components/wp-fast-table/wp-table-sort-by.ts
  12. 13
      frontend/app/components/wp-fast-table/wp-table-sum.ts
  13. 20
      frontend/app/components/wp-fast-table/wp-table-timeline.ts
  14. 14
      frontend/app/components/wp-table/timeline/grid/wp-timeline-grid.directive.ts
  15. 14
      frontend/app/components/wp-table/timeline/header/wp-timeline-header.directive.ts
  16. 20
      frontend/app/components/wp-table/timeline/wp-timeline.ts
  17. 64
      frontend/app/helpers/url-params-helper.ts
  18. 6
      frontend/tests/unit/tests/helpers/url-params-helper-test.js
  19. 14
      spec/lib/api/v3/queries/schemas/query_schema_representer_spec.rb

@ -45,6 +45,8 @@ interface QueryResourceEmbedded {
filters:QueryFilterInstanceResource[];
}
export type TimelineZoomLevel = 'days' | 'weeks' | 'months' | 'quarters' | 'years';
export class QueryResource extends HalResource {
public $embedded:QueryResourceEmbedded;
public id:number;
@ -56,6 +58,7 @@ export class QueryResource extends HalResource {
public starred:boolean;
public sums:boolean;
public timelineVisible:boolean;
public timelineZoomLevel:TimelineZoomLevel;
public showHierarchies:boolean;
public public:boolean;
public project:ProjectResource;

@ -42,7 +42,10 @@ import {WorkPackagesListChecksumService} from "../../wp-list/wp-list-checksum.se
import {WorkPackagesListService} from "../../wp-list/wp-list.service";
import {WorkPackageTableTimelineService} from "../../wp-fast-table/state/wp-table-timeline.service";
import {WorkPackageTableBaseService} from "../../wp-fast-table/state/wp-table-base.service";
import {WorkPackageTableBaseState} from "../../wp-fast-table/wp-table-base";
import {
WorkPackageTableBaseState,
WorkPackageTableQueryState
} from "../../wp-fast-table/wp-table-base";
import {WorkPackageTableRefreshService} from "../../wp-table/wp-table-refresh-request.service";
import {debugLog} from "../../../helpers/debug_output";
@ -116,16 +119,17 @@ function WorkPackagesListController($scope:any,
}
});
setupChangeObserver(wpTableFilters, 'filters');
setupChangeObserver(wpTableGroupBy, 'groupBy');
setupChangeObserver(wpTableSortBy, 'sortBy');
setupChangeObserver(wpTableSum, 'sums');
setupChangeObserver(wpTableTimeline, 'timelineVisible', false);
setupChangeObserver(wpTableHierarchies, 'showHierarchies', false);
setupChangeObserver(wpTableColumns, 'columns', false);
setupChangeObserver(wpTableFilters);
setupChangeObserver(wpTableGroupBy);
setupChangeObserver(wpTableSortBy);
setupChangeObserver(wpTableSum);
setupChangeObserver(wpTableTimeline, false);
setupChangeObserver(wpTableTimeline, false);
setupChangeObserver(wpTableHierarchies, false);
setupChangeObserver(wpTableColumns, false);
}
function setupChangeObserver(service:WorkPackageTableBaseService, name:string, triggerUpdate:boolean = true) {
function setupChangeObserver(service:WorkPackageTableBaseService, triggerUpdate:boolean = true) {
const queryState = states.table.query;
states.table.context.fireOnStateChange(service.state, 'Query loaded')
@ -133,16 +137,10 @@ function WorkPackagesListController($scope:any,
.takeUntil(scopeDestroyed$($scope))
.filter(() => !isAnyDependentStateClear()) // Avoid updating while not all states are initialized
.filter(() => queryState.hasValue())
.filter((stateValue:WorkPackageTableBaseState<any>) => {
// Avoid updating if the query is up-to-date
// (Loaded into query elsewhere)
const queryValue = stateValue.comparerFunction()(queryState.value![name]);
return _.isEqual(queryValue, stateValue.extractedCompareValue) === false;
})
.subscribe((stateValue:WorkPackageTableBaseState<any>) => {
.filter((stateValue:WorkPackageTableQueryState) => stateValue.hasChanged(queryState.value!))
.subscribe((stateValue:WorkPackageTableQueryState) => {
const newQuery = queryState.value!;
newQuery[name] = _.cloneDeep(stateValue.currentQueryValue);
stateValue.applyToQuery(newQuery);
states.table.query.putValue(newQuery);
if (triggerUpdate) {

@ -32,7 +32,8 @@ import {
wpButtonDirective
} from '../wp-buttons.module';
import { WorkPackageTableTimelineService } from "../../wp-fast-table/state/wp-table-timeline.service";
import {ZoomLevel} from "../../wp-table/timeline/wp-timeline";
import {zoomLevelOrder} from "../../wp-table/timeline/wp-timeline";
import {TimelineZoomLevel} from "../../api/api-v3/hal-resources/query-resource.service";
interface TimelineButtonText extends ButtonControllerText {
zoomOut:string;
@ -48,8 +49,8 @@ export class WorkPackageTimelineButtonController extends WorkPackageButtonContro
public text:TimelineButtonText;
public minZoomLevel = ZoomLevel.DAYS;
public maxZoomLevel = ZoomLevel.YEARS;
public minZoomLevel:TimelineZoomLevel = 'days';
public maxZoomLevel:TimelineZoomLevel = 'years';
constructor(public I18n:op.I18n, public wpTableTimeline:WorkPackageTableTimelineService) {
'ngInject';

@ -31,7 +31,7 @@ import {QueryResource} from "../../api/api-v3/hal-resources/query-resource.servi
import {opServicesModule} from "../../../angular-modules";
import {States} from "../../states.service";
import {WorkPackageTableTimelineState} from "./../wp-table-timeline";
import {ZoomLevel} from "../../wp-table/timeline/wp-timeline";
import {zoomLevelOrder} from "../../wp-table/timeline/wp-timeline";
export class WorkPackageTableTimelineService extends WorkPackageTableBaseService {
protected stateName = 'timelineVisible' as TableStateStates;
@ -41,7 +41,7 @@ export class WorkPackageTableTimelineService extends WorkPackageTableBaseService
}
public initialize(query:QueryResource) {
let current = new WorkPackageTableTimelineState(query.timelineVisible, ZoomLevel.DAYS);
let current = new WorkPackageTableTimelineState(query.timelineVisible, query.timelineZoomLevel);
this.state.putValue(current);
}
@ -64,9 +64,13 @@ export class WorkPackageTableTimelineService extends WorkPackageTableBaseService
public updateZoom(delta: number) {
let currentState = this.current;
currentState.zoomLevel += delta;
let idx = zoomLevelOrder.indexOf(this.current.zoomLevel);
idx += delta;
this.state.putValue(currentState);
if (idx >= 0 && idx < zoomLevelOrder.length) {
currentState.zoomLevel = zoomLevelOrder[idx];
this.state.putValue(currentState);
}
}
private get current():WorkPackageTableTimelineState {

@ -26,26 +26,22 @@
// See doc/COPYRIGHT.rdoc for more details.
// ++
export class WorkPackageTableBaseState<T> {
import {QueryResource} from "../api/api-v3/hal-resources/query-resource.service";
export interface WorkPackageTableQueryState {
/**
* Extract the current value from this state and pass it through the comparer function.
* Check whether the state value does not match the query resource's value.
* @param query The current query resource
*/
public get extractedCompareValue():any {
return this.current && this.comparerFunction()(this.current);
}
public get currentQueryValue():any {
return this.current;
}
hasChanged(query:QueryResource):boolean;
/**
* Returns a comparer function for this state's value used to compare state values,
* e.g., as a distinctUntilChanged() key function.
* Apply the current state value to query
*/
public comparerFunction():(current:T) => any {
return (current: T) => current;
}
applyToQuery(query:QueryResource):void;
}
export abstract class WorkPackageTableBaseState<T> {
public current:T;
}

@ -30,9 +30,9 @@ import {QueryColumn} from '../api/api-v3/hal-resources/query-resource.service'
import {QueryResource} from '../api/api-v3/hal-resources/query-resource.service'
import {QuerySchemaResourceInterface} from '../api/api-v3/hal-resources/query-schema-resource.service'
import {Observable} from 'rxjs/Observable';
import {WorkPackageTableBaseState} from "./wp-table-base";
import {WorkPackageTableBaseState, WorkPackageTableQueryState} from "./wp-table-base";
export class WorkPackageTableColumns extends WorkPackageTableBaseState<QueryColumn[]> {
export class WorkPackageTableColumns extends WorkPackageTableBaseState<QueryColumn[]> implements WorkPackageTableQueryState {
// Available columns
public available:QueryColumn[]|undefined;
@ -45,6 +45,14 @@ export class WorkPackageTableColumns extends WorkPackageTableBaseState<QueryColu
this.update(query, schema);
}
public hasChanged(query:QueryResource) {
return !_.isEqual(query.columns, this.current);
}
public applyToQuery(query:QueryResource) {
query.columns = _.cloneDeep(this.current);
}
public update(query:QueryResource|null, schema?:QuerySchemaResourceInterface) {
if (query) {
this.current = angular.copy(query.columns);

@ -36,16 +36,12 @@ import {QueryResource} from '../api/api-v3/hal-resources/query-resource.service'
import {QuerySchemaResourceInterface} from '../api/api-v3/hal-resources/query-schema-resource.service';
import {QueryFilterInstanceSchemaResource} from '../api/api-v3/hal-resources/query-filter-instance-schema-resource.service';
import {HalResource} from "../api/api-v3/hal-resources/hal-resource.service";
import {WorkPackageTableBaseState} from "./wp-table-base";
import {WorkPackageTableBaseState, WorkPackageTableQueryState} from "./wp-table-base";
export class WorkPackageTableFilters extends WorkPackageTableBaseState<QueryFilterInstanceResource[]> {
export class WorkPackageTableFilters extends WorkPackageTableBaseState<QueryFilterInstanceResource[]> implements WorkPackageTableQueryState {
public availableSchemas:QueryFilterInstanceSchemaResource[] = [];
public current:QueryFilterInstanceResource[] = [];
public comparerFunction():(current:QueryFilterInstanceResource[]) => any {
return (current:QueryFilterInstanceResource[]) => current.map((el:HalResource) => el.$plain());
}
constructor(filters:QueryFilterInstanceResource[], schema:QuerySchemaResourceInterface) {
super();
this.current = filters;
@ -54,6 +50,14 @@ export class WorkPackageTableFilters extends WorkPackageTableBaseState<QueryFilt
.elements as QueryFilterInstanceSchemaResource[];
}
public hasChanged(query:QueryResource) {
return !_.isEqual(query.filters, this.current.map((el:HalResource) => el.$plain()));
}
public applyToQuery(query:QueryResource) {
query.filters = _.cloneDeep(this.current);
}
public add(filter:QueryFilterResource) {
let schema = _.find(this.availableSchemas,
schema => (schema.filter.allowedValues as QueryFilterResource[])[0].href === filter.href);

@ -32,16 +32,12 @@ import {
QueryColumn
} from '../api/api-v3/hal-resources/query-resource.service';
import {QuerySchemaResourceInterface} from '../api/api-v3/hal-resources/query-schema-resource.service';
import {WorkPackageTableBaseState} from "./wp-table-base";
import {WorkPackageTableBaseState, WorkPackageTableQueryState} from "./wp-table-base";
export class WorkPackageTableGroupBy extends WorkPackageTableBaseState<QueryGroupByResource | undefined> {
export class WorkPackageTableGroupBy extends WorkPackageTableBaseState<QueryGroupByResource | undefined> implements WorkPackageTableQueryState {
public available:QueryGroupByResource[] = [];
public current:QueryGroupByResource | undefined;
public comparerFunction():(current:QueryGroupByResource|undefined) => any {
return (current:QueryGroupByResource) => current && current.href;
}
constructor(query:QueryResource, schema?:QuerySchemaResourceInterface) {
super();
this.current = angular.copy(query.groupBy);
@ -51,6 +47,14 @@ export class WorkPackageTableGroupBy extends WorkPackageTableBaseState<QueryGrou
}
}
public hasChanged(query:QueryResource) {
return !_.isEqual(query.groupBy, _.get(this.current, 'href'));
}
public applyToQuery(query:QueryResource) {
query.groupBy = _.cloneDeep(this.current);
}
public update(query:QueryResource|null, schema?:QuerySchemaResourceInterface) {
if (query) {
this.current = angular.copy(query.groupBy);

@ -26,8 +26,9 @@
// See doc/COPYRIGHT.rdoc for more details.
// ++
import {WorkPackageTableBaseState} from "./wp-table-base";
export class WorkPackageTableHierarchies extends WorkPackageTableBaseState<boolean> {
import {WorkPackageTableBaseState, WorkPackageTableQueryState} from "./wp-table-base";
import {QueryResource} from "../api/api-v3/hal-resources/query-resource.service";
export class WorkPackageTableHierarchies extends WorkPackageTableBaseState<boolean> implements WorkPackageTableQueryState {
public current:boolean;
public collapsed:{[workPackageId:string]:boolean};
@ -37,6 +38,14 @@ export class WorkPackageTableHierarchies extends WorkPackageTableBaseState<boole
this.collapsed = {};
}
public hasChanged(query:QueryResource) {
return query.showHierarchies !== this.isEnabled;
}
public applyToQuery(query:QueryResource) {
query.showHierarchies = this.isEnabled;
}
public toggle() {
this.current = !this.current;
}

@ -28,6 +28,7 @@
import {WorkPackageCollectionResource} from '../api/api-v3/hal-resources/wp-collection-resource.service'
import {WorkPackageTableBaseState} from "./wp-table-base";
import {QueryResource} from "../api/api-v3/hal-resources/query-resource.service";
export class WorkPackageTablePaginationObject extends WorkPackageTableBaseState<WorkPackageTablePagination> {
constructor(public page:number,

@ -36,10 +36,10 @@ import {
QueryColumn
} from '../api/api-v3/hal-resources/query-resource.service';
import {QuerySchemaResourceInterface} from '../api/api-v3/hal-resources/query-schema-resource.service';
import {WorkPackageTableBaseState} from "./wp-table-base";
import {WorkPackageTableBaseState, WorkPackageTableQueryState} from "./wp-table-base";
import {HalResource} from "../api/api-v3/hal-resources/hal-resource.service";
export class WorkPackageTableSortBy extends WorkPackageTableBaseState<QuerySortByResource[]> {
export class WorkPackageTableSortBy extends WorkPackageTableBaseState<QuerySortByResource[]> implements WorkPackageTableQueryState {
public available: QuerySortByResource[] = [];
public current:QuerySortByResource[] = [];
@ -49,8 +49,12 @@ export class WorkPackageTableSortBy extends WorkPackageTableBaseState<QuerySortB
this.available = angular.copy(schema.sortBy.allowedValues as QuerySortByResource[]);
}
public comparerFunction():(current:QuerySortByResource[]) => any {
return (current:QuerySortByResource[]) => current.map((el:HalResource) => el.href);
public hasChanged(query:QueryResource) {
return !_.isEqual(query.groupBy, this.current.map((el:HalResource) => el.href));
}
public applyToQuery(query:QueryResource) {
query.sortBy = _.cloneDeep(this.current);
}
public addCurrent(sortBy:QuerySortByResource) {

@ -26,8 +26,9 @@
// See doc/COPYRIGHT.rdoc for more details.
// ++
import {WorkPackageTableBaseState} from "./wp-table-base";
export class WorkPackageTableSum extends WorkPackageTableBaseState<boolean> {
import {WorkPackageTableBaseState, WorkPackageTableQueryState} from "./wp-table-base";
import {QueryResource} from "../api/api-v3/hal-resources/query-resource.service";
export class WorkPackageTableSum extends WorkPackageTableBaseState<boolean> implements WorkPackageTableQueryState {
public current:boolean;
constructor(isSum:boolean) {
@ -35,6 +36,14 @@ export class WorkPackageTableSum extends WorkPackageTableBaseState<boolean> {
this.current = isSum;
}
public hasChanged(query:QueryResource) {
return query.sums !== this.isEnabled;
}
public applyToQuery(query:QueryResource) {
query.sums = this.isEnabled;
}
public toggle() {
this.current = !this.current;
}

@ -26,12 +26,26 @@
// See doc/COPYRIGHT.rdoc for more details.
// ++
import {WorkPackageTableBaseState} from "./wp-table-base";
export class WorkPackageTableTimelineState extends WorkPackageTableBaseState<boolean> {
constructor(public visible:boolean, public zoomLevel:number) {
import {WorkPackageTableBaseState, WorkPackageTableQueryState} from "./wp-table-base";
import {QueryResource, TimelineZoomLevel} from "../api/api-v3/hal-resources/query-resource.service";
export class WorkPackageTableTimelineState extends WorkPackageTableBaseState<boolean> implements WorkPackageTableQueryState {
constructor(public visible:boolean, public zoomLevel:TimelineZoomLevel) {
super();
}
public hasChanged(query:QueryResource) {
const visibilityChanged = this.isVisible !== query.timelineVisible;
const zoomLevelChanged = this.zoomLevel !== query.timelineZoomLevel;
return visibilityChanged || zoomLevelChanged;
}
public applyToQuery(query:QueryResource) {
query.timelineVisible = this.isVisible;
query.timelineZoomLevel = this.zoomLevel;
}
public get current():boolean {
return this.isVisible;
}

@ -28,19 +28,19 @@
import {
TimelineViewParameters,
timelineElementCssClass,
ZoomLevel,
calculatePositionValueForDayCount, timelineGridElementCssClass
} from "../wp-timeline";
import {WorkPackageTimelineTableController} from "../container/wp-timeline-container.directive";
import * as moment from 'moment';
import Moment = moment.Moment;
import {openprojectModule} from "../../../../angular-modules";
import {TimelineZoomLevel} from "../../../api/api-v3/hal-resources/query-resource.service";
export class WorkPackageTableTimelineGrid {
public wpTimeline:WorkPackageTimelineTableController;
private activeZoomLevel:ZoomLevel;
private activeZoomLevel:TimelineZoomLevel;
private gridContainer:ng.IAugmentedJQuery;
@ -64,15 +64,15 @@ export class WorkPackageTableTimelineGrid {
this.gridContainer.empty();
switch (vp.settings.zoomLevel) {
case ZoomLevel.DAYS:
case 'days':
return this.renderLabelsDays(vp);
case ZoomLevel.WEEKS:
case 'weeks':
return this.renderLabelsWeeks(vp);
case ZoomLevel.MONTHS:
case 'months':
return this.renderLabelsMonths(vp);
case ZoomLevel.QUARTERS:
case 'quarters':
return this.renderLabelsQuarters(vp);
case ZoomLevel.YEARS:
case 'years':
return this.renderLabelsYears(vp);
}

@ -28,13 +28,13 @@
import {
TimelineViewParameters,
timelineElementCssClass,
ZoomLevel,
calculatePositionValueForDayCount
} from "../wp-timeline";
import {WorkPackageTimelineTableController} from "../container/wp-timeline-container.directive";
import * as moment from 'moment';
import Moment = moment.Moment;
import {openprojectModule} from "../../../../angular-modules";
import {TimelineZoomLevel} from "../../../api/api-v3/hal-resources/query-resource.service";
export const timelineHeaderCSSClass = 'wp-timeline--header-element';
@ -42,7 +42,7 @@ export class WorkPackageTimelineHeaderController {
public wpTimeline:WorkPackageTimelineTableController;
private activeZoomLevel:ZoomLevel;
private activeZoomLevel:TimelineZoomLevel;
private innerHeader:ng.IAugmentedJQuery;
@ -66,15 +66,15 @@ export class WorkPackageTimelineHeaderController {
this.innerHeader.empty();
switch (vp.settings.zoomLevel) {
case ZoomLevel.DAYS:
case 'days':
return this.renderLabelsDays(vp);
case ZoomLevel.WEEKS:
case 'weeks':
return this.renderLabelsWeeks(vp);
case ZoomLevel.MONTHS:
case 'months':
return this.renderLabelsMonths(vp);
case ZoomLevel.QUARTERS:
case 'quarters':
return this.renderLabelsQuarters(vp);
case ZoomLevel.YEARS:
case 'years':
return this.renderLabelsYears(vp);
}

@ -31,6 +31,7 @@ import {
WorkPackageResource
} from "../../api/api-v3/hal-resources/work-package-resource.service";
import Moment = moment.Moment;
import {TimelineZoomLevel} from "../../api/api-v3/hal-resources/query-resource.service";
export const timelineElementCssClass = "timeline-element";
export const timelineGridElementCssClass = "wp-timeline--grid-element";
@ -41,13 +42,14 @@ export const timelineMarkerSelectionStartClass = "selection-start";
*/
export class TimelineViewParametersSettings {
zoomLevel: ZoomLevel = ZoomLevel.DAYS;
zoomLevel: TimelineZoomLevel = 'days';
}
export enum ZoomLevel {
DAYS, WEEKS, MONTHS, QUARTERS, YEARS
}
// Can't properly map the enum to a string aray
export const zoomLevelOrder:TimelineZoomLevel[] = [
'days', 'weeks', 'months', 'quarters', 'years'
];
/**
@ -69,15 +71,15 @@ export class TimelineViewParameters {
get pixelPerDay(): number {
switch (this.settings.zoomLevel) {
case ZoomLevel.DAYS:
case 'days':
return 30;
case ZoomLevel.WEEKS:
case 'weeks':
return 15;
case ZoomLevel.MONTHS:
case 'months':
return 6;
case ZoomLevel.QUARTERS:
case 'quarters':
return 2;
case ZoomLevel.YEARS:
case 'years':
return 0.5;
}
}

@ -26,20 +26,24 @@
// See doc/COPYRIGHT.rdoc for more details.
//++
module.exports = function(PaginationService) {
import {QueryColumn} from "../components/wp-query/query-column";
import {QueryFilterResource} from "../components/api/api-v3/hal-resources/query-filter-resource.service";
import {QuerySortByResource} from "../components/api/api-v3/hal-resources/query-sort-by-resource.service";
import {QueryResource} from "../components/api/api-v3/hal-resources/query-resource.service";
module.exports = function(PaginationService:any) {
var UrlParamsHelper = {
// copied more or less from angular buildUrl
buildQueryString: function(params) {
buildQueryString: function(params:any) {
if (!params) return;
var parts = [];
var parts:string[] = [];
angular.forEach(params, function(value, key) {
if (!value) return;
if (!Array.isArray(value)) value = [value];
angular.forEach(value, function(v) {
if (v !== null && typeof v === 'object') {
v = toJson(v);
v = JSON.stringify(v);
}
parts.push(encodeURIComponent(key) + '=' +
encodeURIComponent(v));
@ -49,8 +53,8 @@ module.exports = function(PaginationService) {
return parts.join('&');
},
encodeQueryJsonParams: function(query, additional) {
var paramsData = {
encodeQueryJsonParams: function(query:QueryResource, additional:any) {
var paramsData:any = {
c: query.columns.map(function(column) { return column.id; })
};
if(!!query.sums) {
@ -61,6 +65,7 @@ module.exports = function(PaginationService) {
paramsData.tv = query.timelineVisible;
}
paramsData.tzl = query.timelineZoomLevel;
paramsData.hi = !!query.showHierarchies;
if(query.groupBy) {
@ -69,13 +74,13 @@ module.exports = function(PaginationService) {
if(query.sortBy) {
paramsData.t = query
.sortBy
.map(function(sort) { return sort.id.replace('-', ':') })
.map(function(sort:QuerySortByResource) { return sort.id.replace('-', ':') })
.join();
}
if(query.filters && query.filters.length) {
paramsData.f = query
.filters
.map(function(filter) {
.map((filter:any) => {
var id = filter.id;
var operator = filter.operator.id;
@ -96,8 +101,8 @@ module.exports = function(PaginationService) {
return JSON.stringify(paramsData);
},
buildV3GetQueryFromJsonParams: function(updateJson) {
var queryData = {
buildV3GetQueryFromJsonParams: function(updateJson:any) {
var queryData:any = {
pageSize: PaginationService.getPerPage()
}
@ -108,7 +113,7 @@ module.exports = function(PaginationService) {
var properties = JSON.parse(updateJson);
if(properties.c) {
queryData["columns[]"] = properties.c.map(function(column) { return column; });
queryData["columns[]"] = properties.c.map((column:any) => column);
}
if(!!properties.s) {
queryData.showSums = properties.s;
@ -117,6 +122,10 @@ module.exports = function(PaginationService) {
queryData.timelineVisible = properties.tv;
}
if (properties.tzl) {
queryData.timelineZoomLevel = properties.tzl;
}
if(properties.hi === false || properties.hi === true) {
queryData.showHierarchies = properties.hi;
}
@ -127,7 +136,7 @@ module.exports = function(PaginationService) {
// Filters
if(properties.f) {
var filters = properties.f.map(function(urlFilter) {
var filters = properties.f.map(function(urlFilter:any) {
var attributes = {
operator: decodeURIComponent(urlFilter.o)
}
@ -137,7 +146,7 @@ module.exports = function(PaginationService) {
var vs = Array.isArray(urlFilter.v) ? urlFilter.v : [urlFilter.v];
angular.extend(attributes, { values: vs });
}
filterData = {};
const filterData:any = {};
filterData[urlFilter.n] = attributes;
return filterData;
@ -148,7 +157,7 @@ module.exports = function(PaginationService) {
// Sortation
if(properties.t) {
queryData.sortBy = JSON.stringify(properties.t.split(',').map(function(sort) { return sort.split(':') }));
queryData.sortBy = JSON.stringify(properties.t.split(',').map((sort:any) => sort.split(':')));
}
// Pagination
@ -162,13 +171,14 @@ module.exports = function(PaginationService) {
return queryData;
},
buildV3GetQueryFromQueryResource: function(query, additionalParams) {
var queryData = {};
buildV3GetQueryFromQueryResource: function(query:QueryResource, additionalParams:any) {
var queryData:any = {};
queryData["columns[]"] = query.columns.map(function(column) { return column.id; });
queryData["columns[]"] = query.columns.map((column:any) => column.id);
queryData.showSums = query.sums;
queryData.timelineVisible = query.timelineVisible;
queryData.timelineZoomLevel = query.timelineZoomLevel;
queryData.showHierarchies = query.showHierarchies;
if(query.groupBy) {
@ -176,18 +186,14 @@ module.exports = function(PaginationService) {
}
// Filters
filters = query.filters.map(function(filter) {
var id = filter.filter.$href;
const filters = query.filters.map((filter:any) => {
let id = filter.filter.$href;
id = id.substring(id.lastIndexOf('/') + 1, id.length);
var operator = filter.operator.id;
var values = _.map(filter.values, UrlParamsHelper.queryFilterValueToParam);
var filterHash = {};
filterHash[id] = { operator: operator,
values: values }
const operator = filter.operator.id;
const values = _.map(filter.values, UrlParamsHelper.queryFilterValueToParam);
const filterHash:any = {};
filterHash[id] = { operator: operator, values: values };
return filterHash;
});
@ -197,13 +203,13 @@ module.exports = function(PaginationService) {
// Sortation
queryData.sortBy = JSON.stringify(query
.sortBy
.map(function(sort) { return sort.id.split('-') }));
.map(function(sort:QuerySortByResource) { return sort.id.split('-') }));
return angular.extend(queryData, additionalParams);
},
queryFilterValueToParam: function(value) {
queryFilterValueToParam: function(value:any) {
if (typeof(value) === 'boolean') {
return value ? 't': 'f';
}

@ -90,6 +90,7 @@ describe('UrlParamsHelper', function() {
name: 'knoblauch soße',
sums: true,
timelineVisible: true,
timelineZoomLevel: 'days',
showHierarchies: true,
columns: [{ id: 'type' }, { id: 'status' }, { id: 'soße' }],
groupBy: {
@ -109,7 +110,7 @@ describe('UrlParamsHelper', function() {
it('should encode query to params JSON', function() {
var encodedJSON = UrlParamsHelper.encodeQueryJsonParams(query, additional);
var expectedJSON = "{\"c\":[\"type\",\"status\",\"soße\"],\"s\":true,\"tv\":true,\"hi\":true,\"g\":\"status\",\"t\":\"type:desc\",\"f\":[{\"n\":\"soße\",\"o\":\"%3D\",\"v\":[\"knoblauch\"]},{\"n\":\"created_at\",\"o\":\"%3Ct-\",\"v\":[\"5\"]}],\"pa\":10,\"pp\":100}";
var expectedJSON = "{\"c\":[\"type\",\"status\",\"soße\"],\"s\":true,\"tv\":true,\"tzl\":\"days\",\"hi\":true,\"g\":\"status\",\"t\":\"type:desc\",\"f\":[{\"n\":\"soße\",\"o\":\"%3D\",\"v\":[\"knoblauch\"]},{\"n\":\"created_at\",\"o\":\"%3Ct-\",\"v\":[\"5\"]}],\"pa\":10,\"pp\":100}";
expect(encodedJSON).to.eq(expectedJSON);
});
});
@ -118,7 +119,7 @@ describe('UrlParamsHelper', function() {
var params;
beforeEach(function() {
params = "{\"c\":[\"type\",\"status\",\"soße\"],\"s\":true,\"tv\":true,\"hi\":true,\"g\":\"status\",\"t\":\"type:desc,status:asc\",\"f\":[{\"n\":\"soße\",\"o\":\"%3D\",\"v\":[\"knoblauch\"]},{\"n\":\"created_at\",\"o\":\"%3Ct-\",\"v\":[\"5\"]}],\"pa\":10,\"pp\":100}";
params = "{\"c\":[\"type\",\"status\",\"soße\"],\"s\":true,\"tv\":true,\"tzl\":\"days\",\"hi\":true,\"g\":\"status\",\"t\":\"type:desc,status:asc\",\"f\":[{\"n\":\"soße\",\"o\":\"%3D\",\"v\":[\"knoblauch\"]},{\"n\":\"created_at\",\"o\":\"%3Ct-\",\"v\":[\"5\"]}],\"pa\":10,\"pp\":100}";
});
it('should decode query params to object', function() {
@ -129,6 +130,7 @@ describe('UrlParamsHelper', function() {
showSums: true,
timelineVisible: true,
showHierarchies: true,
timelineZoomLevel: 'days',
groupBy: 'status',
filters: JSON.stringify([
{

@ -206,6 +206,20 @@ describe ::API::V3::Queries::Schemas::QuerySchemaRepresenter do
it_behaves_like 'has no visibility property'
end
describe 'timelineZoomLevel' do
let(:path) { 'timelineZoomLevel' }
it_behaves_like 'has basic schema properties' do
let(:type) { 'String' }
let(:name) { Query.human_attribute_name('timeline_zoom_level') }
let(:required) { false }
let(:writable) { true }
let(:has_default) { true }
end
it_behaves_like 'has no visibility property'
end
describe 'show hierarchies' do
let(:path) { 'showHierarchies' }

Loading…
Cancel
Save