Browse Source

Minor improvements to query editor.

pull/473/head
Sebastian 7 years ago
parent
commit
bf3faa1524
  1. 2
      frontend/app/features/content/pages/contents/contents-page.component.html
  2. 2
      frontend/app/features/content/shared/contents-selector.component.html
  3. 4
      frontend/app/framework/angular/forms/date-time-editor.component.html
  4. 10
      frontend/app/framework/angular/forms/date-time-editor.component.ts
  5. 12
      frontend/app/framework/angular/forms/dropdown.component.ts
  6. 20
      frontend/app/shared/components/queries/filter-comparison.component.html
  7. 6
      frontend/app/shared/components/queries/filter-comparison.component.scss
  8. 16
      frontend/app/shared/components/queries/sorting.component.ts
  9. 2
      frontend/app/shared/components/search-form.component.ts
  10. 95
      frontend/app/shared/state/query.ts
  11. 4
      frontend/app/theme/_forms.scss

2
frontend/app/features/content/pages/contents/contents-page.component.html

@ -15,7 +15,7 @@
</button> </button>
</div> </div>
<div class="col pl-1"> <div class="col pl-1">
<sqx-search-form formClass="form" placeholder="Search for content" <sqx-search-form formClass="form" placeholder="Fulltext search"
[query]="contentsState.contentsQuery | async" [query]="contentsState.contentsQuery | async"
[queries]="queries" [queries]="queries"
[queryModel]="queryModel" [queryModel]="queryModel"

2
frontend/app/features/content/shared/contents-selector.component.html

@ -22,7 +22,7 @@
</button> </button>
</div> </div>
<div class="col pl-1"> <div class="col pl-1">
<sqx-search-form formClass="form" placeholder="Search for content" <sqx-search-form formClass="form" placeholder="Fulltext search"
[query]="contentsState.contentsQuery | async" [query]="contentsState.contentsQuery | async"
[queryModel]="queryModel" [queryModel]="queryModel"
(queryChange)="search($event)"> (queryChange)="search($event)">

4
frontend/app/framework/angular/forms/date-time-editor.component.html

@ -13,12 +13,12 @@
<i class="icon-close"></i> <i class="icon-close"></i>
</button> </button>
</div> </div>
<div class="form-group" *ngIf="showTime && !hideDateButtons"> <div class="form-group" *ngIf="showTime && shouldShowDateButtons">
<button type="button" class="btn btn-text-secondary" [disabled]="snapshot.isDisabled" (click)="writeNow()" title="Use Now (UTC)"> <button type="button" class="btn btn-text-secondary" [disabled]="snapshot.isDisabled" (click)="writeNow()" title="Use Now (UTC)">
Now Now
</button> </button>
</div> </div>
<div class="form-group" *ngIf="!showTime && !hideDateButtons"> <div class="form-group" *ngIf="!showTime && shouldShowDateButtons">
<button type="button" class="btn btn-text-secondary" [disabled]="snapshot.isDisabled" (click)="writeNow()" title="Use Today (UTC)"> <button type="button" class="btn btn-text-secondary" [disabled]="snapshot.isDisabled" (click)="writeNow()" title="Use Today (UTC)">
Today Today
</button> </button>

10
frontend/app/framework/angular/forms/date-time-editor.component.ts

@ -36,6 +36,7 @@ export class DateTimeEditorComponent extends StatefulControlComponent<{}, string
private picker: any; private picker: any;
private timeValue: moment.Moment | null = null; private timeValue: moment.Moment | null = null;
private dateValue: moment.Moment | null = null; private dateValue: moment.Moment | null = null;
private hideDateButtonsSettings: boolean;
private suppressEvents = false; private suppressEvents = false;
@Input() @Input()
@ -47,13 +48,18 @@ export class DateTimeEditorComponent extends StatefulControlComponent<{}, string
@Input() @Input()
public hideClear: boolean; public hideClear: boolean;
@Input()
public hideDateButtons: boolean;
@ViewChild('dateInput', { static: false }) @ViewChild('dateInput', { static: false })
public dateInput: ElementRef; public dateInput: ElementRef;
public timeControl = new FormControl(); public timeControl = new FormControl();
public dateControl = new FormControl(); public dateControl = new FormControl();
public hideDateButtons: boolean; public get shouldShowDateButtons() {
return !this.hideDateButtonsSettings && !this.hideDateButtons;
}
public get showTime() { public get showTime() {
return this.mode === 'DateTime'; return this.mode === 'DateTime';
@ -66,7 +72,7 @@ export class DateTimeEditorComponent extends StatefulControlComponent<{}, string
constructor(changeDetector: ChangeDetectorRef, uiOptions: UIOptions) { constructor(changeDetector: ChangeDetectorRef, uiOptions: UIOptions) {
super(changeDetector, {}); super(changeDetector, {});
this.hideDateButtons = !!uiOptions.get('hideDateButtons'); this.hideDateButtonsSettings = !!uiOptions.get('hideDateButtons');
} }
public ngOnInit() { public ngOnInit() {

12
frontend/app/framework/angular/forms/dropdown.component.ts

@ -107,15 +107,11 @@ export class DropdownComponent extends StatefulControlComponent<State, any[]> im
public ngAfterContentInit() { public ngAfterContentInit() {
if (this.templates.length === 1) { if (this.templates.length === 1) {
this.templateItem = this.templateSelection = this.templates.first; this.templateItem = this.templates.first;
this.templateSelection = this.templates.first;
} else { } else {
this.templates.forEach(template => { this.templateItem = this.templates.first;
if (template.name === 'selection') { this.templateSelection = this.templates.last;
this.templateSelection = template;
} else {
this.templateItem = template;
}
});
} }
if (this.templateItem) { if (this.templateItem) {

20
frontend/app/shared/components/queries/filter-comparison.component.html

@ -1,15 +1,23 @@
<div class="row no-gutters mb-1" *ngIf="fieldModel"> <div class="row no-gutters mb-1" *ngIf="fieldModel">
<div class="col-auto path"> <div class="col-auto path">
<select class="form-control" [ngModel]="filter.path" (ngModelChange)="changePath($event)"> <sqx-dropdown [items]="model.fields | sqxKeys" [ngModel]="filter.path" (ngModelChange)="changePath($event)" [canSearch]="false">
<option *ngFor="let fieldName of model.fields | sqxKeys" [ngValue]="fieldName">{{fieldName}}</option> <ng-template let-field="$implicit">
</select> <div>{{model.fields[field].displayName}}</div>
<sqx-form-hint>{{model.fields[field].description}}</sqx-form-hint>
</ng-template>
<ng-template let-field="$implicit">
{{model.fields[field].displayName}}
</ng-template>
</sqx-dropdown>
</div> </div>
<div class="col-auto pl-2"> <div class="col-auto operator pl-1">
<select class="form-control" [ngModel]="filter.op" (ngModelChange)="changeOp($event)"> <select class="form-control" [ngModel]="filter.op" (ngModelChange)="changeOp($event)">
<option *ngFor="let operator of fieldModel.operators" [ngValue]="operator.value">{{operator.name || operator.value}}</option> <option *ngFor="let operator of fieldModel.operators" [ngValue]="operator.value">{{operator.name || operator.value}}</option>
</select> </select>
</div> </div>
<div class="col pl-2" *ngIf="!noValue" [ngSwitch]="fieldModel.type"> <div class="col pl-1" *ngIf="!noValue" [ngSwitch]="fieldModel.type">
<ng-container *ngSwitchCase="'boolean'"> <ng-container *ngSwitchCase="'boolean'">
<input type="checkbox" class="form-control" <input type="checkbox" class="form-control"
[ngModel]="filter.value" [ngModel]="filter.value"
@ -17,12 +25,14 @@
</ng-container> </ng-container>
<ng-container *ngSwitchCase="'date'"> <ng-container *ngSwitchCase="'date'">
<sqx-date-time-editor mode="Date" <sqx-date-time-editor mode="Date"
[hideDateButtons]="true"
[ngModel]="filter.value" [ngModel]="filter.value"
(ngModelChange)="changeValue($event)"> (ngModelChange)="changeValue($event)">
</sqx-date-time-editor> </sqx-date-time-editor>
</ng-container> </ng-container>
<ng-container *ngSwitchCase="'datetime'"> <ng-container *ngSwitchCase="'datetime'">
<sqx-date-time-editor mode="DateTime" <sqx-date-time-editor mode="DateTime"
[hideDateButtons]="true"
[ngModel]="filter.value" [ngModel]="filter.value"
(ngModelChange)="changeValue($event)"> (ngModelChange)="changeValue($event)">
</sqx-date-time-editor> </sqx-date-time-editor>

6
frontend/app/shared/components/queries/filter-comparison.component.scss

@ -2,5 +2,9 @@
@import '_mixins'; @import '_mixins';
.path { .path {
max-width: 12rem; width: 10rem;
}
.operator {
width: 10rem;
} }

16
frontend/app/shared/components/queries/sorting.component.ts

@ -15,11 +15,19 @@ import { QueryModel, QuerySorting } from '@app/shared/internal';
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<div class="form-inline"> <div class="form-inline">
<select class="form-control mr-2" [ngModel]="sorting.path" (ngModelChange)="changePath($event)"> <sqx-dropdown [items]="model.fields | sqxKeys" [ngModel]="sorting.path" (ngModelChange)="changePath($event)" [canSearch]="false">
<option *ngFor="let fieldName of model.fields | sqxKeys" [ngValue]="fieldName">{{fieldName}}</option> <ng-template let-field="$implicit">
</select> <div>{{model.fields[field].displayName}}</div>
<sqx-form-hint>{{model.fields[field].description}}</sqx-form-hint>
</ng-template>
<ng-template let-field="$implicit">
{{model.fields[field].displayName}}
</ng-template>
</sqx-dropdown>
<select class="form-control mr-2" [ngModel]="sorting.order" (ngModelChange)="changeOrder($event)"> <select class="form-control ml-1" [ngModel]="sorting.order" (ngModelChange)="changeOrder($event)">
<option>ascending</option> <option>ascending</option>
<option>descending</option> <option>descending</option>
</select> </select>

2
frontend/app/shared/components/search-form.component.ts

@ -56,7 +56,7 @@ export class SearchFormComponent implements OnChanges {
public saveQueryDialog = new DialogModel(); public saveQueryDialog = new DialogModel();
public saveQueryForm = new SaveQueryForm(this.formBuilder); public saveQueryForm = new SaveQueryForm(this.formBuilder);
public searchDialog = new DialogModel(); public searchDialog = new DialogModel(true);
public hasFilter: boolean; public hasFilter: boolean;

95
frontend/app/shared/state/query.ts

@ -43,11 +43,19 @@ export interface QueryFieldModel {
// Extra values. // Extra values.
extra?: any; extra?: any;
// The optional display name for the field.
displayName?: string;
// The optional description for the field.
description?: string;
} }
type QueryModelFields = { [name: string]: QueryFieldModel };
export interface QueryModel { export interface QueryModel {
// All available fields. // All available fields.
fields: { [name: string]: QueryFieldModel }; fields: QueryModelFields;
} }
export type FilterNode = FilterComparison | FilterLogical; export type FilterNode = FilterComparison | FilterLogical;
@ -121,25 +129,25 @@ export function hasFilter(query?: Query) {
} }
const EqualOperators: ReadonlyArray<FilterOperator> = [ const EqualOperators: ReadonlyArray<FilterOperator> = [
{ name: '==', value: 'eq' }, { name: 'is equals to', value: 'eq' },
{ name: '!=', value: 'ne' } { name: 'is not equals to', value: 'ne' }
]; ];
const CompareOperator: ReadonlyArray<FilterOperator> = [ const CompareOperator: ReadonlyArray<FilterOperator> = [
{ name: '<', value: 'lt' }, { name: 'is less than', value: 'lt' },
{ name: '<=', value: 'le' }, { name: 'is less than or equals to', value: 'le' },
{ name: '>', value: 'gt' }, { name: 'is greater than', value: 'gt' },
{ name: '>=', value: 'ge' } { name: 'is greater than or equals to', value: 'ge' }
]; ];
const StringOperators: ReadonlyArray<FilterOperator> = [ const StringOperators: ReadonlyArray<FilterOperator> = [
{ name: 'T*', value: 'startsWith' }, { name: 'starts with', value: 'startsWith' },
{ name: '*T', value: 'endsWith' }, { name: 'ends with', value: 'endsWith' },
{ name: '*T*', value: 'contains' } { name: 'contains', value: 'contains' }
]; ];
const ArrayOperators: ReadonlyArray<FilterOperator> = [ const ArrayOperators: ReadonlyArray<FilterOperator> = [
{ value: 'empty', noValue: true } { value: 'is empty', noValue: true }
]; ];
const TypeBoolean: QueryFieldModel = { const TypeBoolean: QueryFieldModel = {
@ -177,23 +185,48 @@ const TypeTags: QueryFieldModel = {
operators: EqualOperators operators: EqualOperators
}; };
const DEFAULT_FIELDS: QueryModelFields = {
created: {
...TypeDateTime,
displayName: 'Created',
description: 'The date time when the content item was created.'
},
createdBy: {
...TypeString,
displayName: 'Created by',
description: 'The user who created the content item.'
},
lastModified: {
...TypeDateTime,
displayName: 'Updated',
description: 'The date time when the content item was updated the last time.'
},
lastModifiedBy: {
...TypeString,
displayName: 'Updated by',
description: 'The user who updated the content item the last time.'
},
version: {
...TypeNumber,
displayName: 'Version',
description: 'The version of the content item'
}
};
export function queryModelFromSchema(schema: SchemaDetailsDto, languages: ReadonlyArray<LanguageDto>, statuses: ReadonlyArray<StatusInfo> | undefined) { export function queryModelFromSchema(schema: SchemaDetailsDto, languages: ReadonlyArray<LanguageDto>, statuses: ReadonlyArray<StatusInfo> | undefined) {
const languagesCodes = languages.map(x => x.iso2Code); const languagesCodes = languages.map(x => x.iso2Code);
const invariantCodes = ['iv'];
const model: QueryModel = { const model: QueryModel = {
fields: {} fields: { ...DEFAULT_FIELDS }
}; };
model.fields['created'] = TypeDateTime;
model.fields['createdBy'] = TypeString;
model.fields['lastModified'] = TypeDateTime;
model.fields['lastModifiedBy'] = TypeString;
model.fields['version'] = TypeNumber;
if (statuses) { if (statuses) {
model.fields['status'] = { ...TypeStatus, extra: statuses }; model.fields['status'] = {
...TypeStatus,
displayName: 'Status',
description: 'The status of the content item.',
extra: statuses
};
} }
for (const field of schema.fields) { for (const field of schema.fields) {
@ -216,10 +249,22 @@ export function queryModelFromSchema(schema: SchemaDetailsDto, languages: Readon
} }
if (type) { if (type) {
const codes = field.isLocalizable ? languagesCodes : invariantCodes; if (field.isLocalizable) {
for (const code of languagesCodes) {
for (const code of codes) { const infos = {
model.fields[`data.${field.name}.${code}`] = type; displayName: `${field.displayName} (${code})`,
description: `The '${field.displayName}' field of the content item (localized).`
};
model.fields[`data.${field.name}.${code}`] = { ...type, ...infos };
}
} else {
const infos = {
displayName: field.displayName,
description: `The '${field.displayName}' field of the content item.`
};
model.fields[`data.${field.name}.iv`] = { ...type, ...infos };
} }
} }
} }

4
frontend/app/theme/_forms.scss

@ -165,6 +165,10 @@
background: $color-theme-blue; background: $color-theme-blue;
border: 0; border: 0;
color: $color-dark-foreground; color: $color-dark-foreground;
& * {
color: $color-dark-foreground !important;
}
} }
} }
} }

Loading…
Cancel
Save