mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-01-23 01:53:00 +00:00
feat: add easy way to configure search filtering for nested properties
This commit is contained in:
committed by
Rainer Killinger
parent
e75a46633c
commit
2220ab24b3
@@ -1 +1,2 @@
|
|||||||
src/app/_helpers/data
|
src/app/_helpers/data
|
||||||
|
node_modules
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"ecmaVersion": 2020,
|
"ecmaVersion": 2020,
|
||||||
"sourceType": "module",
|
"sourceType": "module",
|
||||||
"project": ["tsconfig.json", "e2e/tsconfig.e2e.json"],
|
"project": ["tsconfig.json", "tsconfig.spec.json", "e2e/tsconfig.e2e.json"],
|
||||||
"createDefaultProgram": true
|
"createDefaultProgram": true
|
||||||
},
|
},
|
||||||
"extends": [
|
"extends": [
|
||||||
@@ -44,7 +44,6 @@
|
|||||||
],
|
],
|
||||||
"unicorn/no-nested-ternary": "off",
|
"unicorn/no-nested-ternary": "off",
|
||||||
"unicorn/better-regex": "off",
|
"unicorn/better-regex": "off",
|
||||||
|
|
||||||
"jsdoc/no-types": "error",
|
"jsdoc/no-types": "error",
|
||||||
"jsdoc/require-param": "off",
|
"jsdoc/require-param": "off",
|
||||||
"jsdoc/require-param-description": "error",
|
"jsdoc/require-param-description": "error",
|
||||||
@@ -52,7 +51,6 @@
|
|||||||
"jsdoc/require-returns": "off",
|
"jsdoc/require-returns": "off",
|
||||||
"jsdoc/require-param-type": "off",
|
"jsdoc/require-param-type": "off",
|
||||||
"jsdoc/require-returns-type": "off",
|
"jsdoc/require-returns-type": "off",
|
||||||
|
|
||||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||||
"@typescript-eslint/no-unused-vars": [
|
"@typescript-eslint/no-unused-vars": [
|
||||||
"error",
|
"error",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2022 StApps
|
* Copyright (C) 2023 StApps
|
||||||
* This program is free software: you can redistribute it and/or modify it
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
* under the terms of the GNU General Public License as published by the Free
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
* Software Foundation, version 3.
|
* Software Foundation, version 3.
|
||||||
@@ -54,14 +54,14 @@ describe('context menu', function () {
|
|||||||
|
|
||||||
it('should truncate categories', function () {
|
it('should truncate categories', function () {
|
||||||
cy.get('stapps-context').within(() => {
|
cy.get('stapps-context').within(() => {
|
||||||
cy.contains('ion-item', '(4) Universitätsveranstaltung').should('not.exist');
|
cy.contains('ion-item', '(1) Universitätsveranstaltung').should('not.exist');
|
||||||
cy.get('.context-filter > ion-button').click();
|
cy.get('.context-filter > ion-button').click();
|
||||||
cy.contains('ion-item', '(4) Universitätsveranstaltung').should('exist');
|
cy.contains('ion-item', '(4) Universitätsveranstaltung').should('exist');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should truncate long category items', function () {
|
it('should truncate long category items', function () {
|
||||||
cy.contains('ion-list', 'Kategorien | Akademische Veranstaltung').within(() => {
|
cy.contains('ion-list', 'Akademische Veranstaltung / Kategorien').within(() => {
|
||||||
cy.contains('ion-item', '(1) Tutorium').should('not.exist');
|
cy.contains('ion-item', '(1) Tutorium').should('not.exist');
|
||||||
cy.get('div > ion-button').click();
|
cy.get('div > ion-button').click();
|
||||||
cy.contains('ion-item', '(1) Tutorium').should('exist');
|
cy.contains('ion-item', '(1) Tutorium').should('exist');
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"sourceMap": false,
|
"sourceMap": false,
|
||||||
"types": ["cypress"]
|
"types": ["cypress"]
|
||||||
}
|
},
|
||||||
|
"exclude": []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||||
import {CdkVirtualForOf, CdkVirtualScrollViewport, VirtualScrollStrategy} from '@angular/cdk/scrolling';
|
import {CdkVirtualForOf, CdkVirtualScrollViewport, VirtualScrollStrategy} from '@angular/cdk/scrolling';
|
||||||
import {BehaviorSubject, Subject, Subscription, takeUntil, timer} from 'rxjs';
|
import {BehaviorSubject, Subject, Subscription, takeUntil, timer} from 'rxjs';
|
||||||
import {debounceTime, distinctUntilChanged, tap} from 'rxjs/operators';
|
import {debounceTime, distinctUntilChanged, tap} from 'rxjs/operators';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2022 StApps
|
* Copyright (C) 2023 StApps
|
||||||
* This program is free software: you can redistribute it and/or modify it
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
* under the terms of the GNU General Public License as published by the Free
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
* Software Foundation, version 3.
|
* Software Foundation, version 3.
|
||||||
@@ -159,6 +159,11 @@ describe('ContextMenuComponent', async () => {
|
|||||||
{
|
{
|
||||||
field: 'type',
|
field: 'type',
|
||||||
buckets: [{count: 10, key: 'date series', checked: true}],
|
buckets: [{count: 10, key: 'date series', checked: true}],
|
||||||
|
info: {
|
||||||
|
onlyOnType: SCThingType.AcademicEvent,
|
||||||
|
field: 'date series',
|
||||||
|
sortOrder: 0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -210,7 +215,7 @@ function getFilterContextType(): FilterContext {
|
|||||||
compact: false,
|
compact: false,
|
||||||
options: facetsMock
|
options: facetsMock
|
||||||
.filter(facet => facet.buckets.length > 0)
|
.filter(facet => facet.buckets.length > 0)
|
||||||
.map(facet => {
|
.map((facet, i) => {
|
||||||
return {
|
return {
|
||||||
buckets: facet.buckets.map(bucket => {
|
buckets: facet.buckets.map(bucket => {
|
||||||
return {
|
return {
|
||||||
@@ -222,6 +227,11 @@ function getFilterContextType(): FilterContext {
|
|||||||
compact: false,
|
compact: false,
|
||||||
field: facet.field,
|
field: facet.field,
|
||||||
onlyOnType: facet.onlyOnType,
|
onlyOnType: facet.onlyOnType,
|
||||||
|
info: {
|
||||||
|
onlyOnType: facet.onlyOnType,
|
||||||
|
field: facet.field,
|
||||||
|
sortOrder: i,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
|
|||||||
import {SCLanguage, SCThingTranslator, SCThingType, SCTranslations} from '@openstapps/core';
|
import {SCLanguage, SCThingTranslator, SCThingType, SCTranslations} from '@openstapps/core';
|
||||||
import {Subscription} from 'rxjs';
|
import {Subscription} from 'rxjs';
|
||||||
import {ContextMenuService} from './context-menu.service';
|
import {ContextMenuService} from './context-menu.service';
|
||||||
import {FilterContext, SortContext, SortContextOption} from './context-type';
|
import {FilterContext, FilterFacet, SortContext, SortContextOption} from './context-type';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The context menu
|
* The context menu
|
||||||
@@ -49,6 +49,19 @@ export class ContextMenuComponent implements OnDestroy {
|
|||||||
*/
|
*/
|
||||||
filterOption: FilterContext;
|
filterOption: FilterContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Picks facets based on the compact filter option and sorts
|
||||||
|
* them based on
|
||||||
|
*
|
||||||
|
* No specific type => Type name alphabetically => Bucket count
|
||||||
|
*/
|
||||||
|
get facets(): FilterFacet[] {
|
||||||
|
const options = this.filterOption.compact
|
||||||
|
? this.filterOption.options.slice(0, this.compactFilterOptionCount)
|
||||||
|
: this.filterOption.options;
|
||||||
|
return options.filter(it => it.buckets.length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Possible languages to be used for translation
|
* Possible languages to be used for translation
|
||||||
*/
|
*/
|
||||||
@@ -102,18 +115,6 @@ export class ContextMenuComponent implements OnDestroy {
|
|||||||
this.contextMenuService.contextFilterChanged(this.filterOption);
|
this.contextMenuService.contextFilterChanged(this.filterOption);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns translated property name
|
|
||||||
*/
|
|
||||||
getTranslatedPropertyName(property: string, onlyForType?: SCThingType): string {
|
|
||||||
return (
|
|
||||||
this.translator.translatedPropertyNames(
|
|
||||||
onlyForType ?? SCThingType.AcademicEvent,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
) as any
|
|
||||||
)[property];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns translated property value
|
* Returns translated property value
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
~ Copyright (C) 2022 StApps
|
~ Copyright (C) 2023 StApps
|
||||||
~ This program is free software: you can redistribute it and/or modify it
|
~ This program is free software: you can redistribute it and/or modify it
|
||||||
~ under the terms of the GNU General Public License as published by the Free
|
~ under the terms of the GNU General Public License as published by the Free
|
||||||
~ Software Foundation, version 3.
|
~ Software Foundation, version 3.
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
<ion-icon *ngIf="!sortOption.reversed" name="arrow_upward"></ion-icon>
|
<ion-icon *ngIf="!sortOption.reversed" name="arrow_upward"></ion-icon>
|
||||||
</span>
|
</span>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
<ion-radio slot="end" [value]="i"> </ion-radio>
|
<ion-radio slot="end" [value]="i"></ion-radio>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</ion-radio-group>
|
</ion-radio-group>
|
||||||
</ion-list>
|
</ion-list>
|
||||||
@@ -55,31 +55,17 @@
|
|||||||
</ion-button>
|
</ion-button>
|
||||||
</ion-list-header>
|
</ion-list-header>
|
||||||
|
|
||||||
<ion-list
|
<ion-list class="filter-group" *ngFor="let facet of facets">
|
||||||
class="filter-group"
|
<div>
|
||||||
*ngFor="
|
|
||||||
let facet of !filterOption.compact
|
|
||||||
? filterOption.options.slice(0, compactFilterOptionCount)
|
|
||||||
: filterOption.options
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<div *ngIf="!facet.field.includes('.')">
|
|
||||||
<ion-list-header class="h3">
|
<ion-list-header class="h3">
|
||||||
<ion-label>
|
<ion-label>
|
||||||
{{
|
<span *ngIf="facet.info.onlyOnType"
|
||||||
(facet.onlyOnType
|
><b>{{ facet.info.onlyOnType | titlecase }}</b> /
|
||||||
? getTranslatedPropertyName(facet.field, facet.onlyOnType)
|
</span>
|
||||||
: getTranslatedPropertyName(facet.field)
|
{{ facet.info.field | titlecase }}
|
||||||
) | titlecase
|
|
||||||
}}
|
|
||||||
{{
|
|
||||||
facet.onlyOnType
|
|
||||||
? ' | ' + (getTranslatedPropertyValue(facet.onlyOnType, 'type') | titlecase)
|
|
||||||
: ''
|
|
||||||
}}
|
|
||||||
</ion-label>
|
</ion-label>
|
||||||
</ion-list-header>
|
</ion-list-header>
|
||||||
<div *ngIf="facet.buckets.length > 0">
|
<div>
|
||||||
<ion-item
|
<ion-item
|
||||||
*ngFor="
|
*ngFor="
|
||||||
let bucket of !facet.compact
|
let bucket of !facet.compact
|
||||||
|
|||||||
@@ -1,14 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 StApps
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
import {TestBed} from '@angular/core/testing';
|
import {TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
import {ContextMenuService} from './context-menu.service';
|
import {ContextMenuService} from './context-menu.service';
|
||||||
import {SCFacet} from '@openstapps/core';
|
import {SCFacet} from '@openstapps/core';
|
||||||
import {FilterContext, SortContext} from './context-type';
|
import {FilterContext, SortContext} from './context-type';
|
||||||
|
import {ThingTranslateModule} from '../../../translation/thing-translate.module';
|
||||||
|
import {TranslateModule} from '@ngx-translate/core';
|
||||||
|
|
||||||
describe('ContextMenuService', () => {
|
describe('ContextMenuService', () => {
|
||||||
let service: ContextMenuService;
|
let service: ContextMenuService;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
|
imports: [ThingTranslateModule.forRoot(), TranslateModule.forRoot()],
|
||||||
providers: [ContextMenuService],
|
providers: [ContextMenuService],
|
||||||
});
|
});
|
||||||
service = TestBed.inject(ContextMenuService);
|
service = TestBed.inject(ContextMenuService);
|
||||||
@@ -123,6 +141,10 @@ const filterContext: FilterContext = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
field: 'type',
|
field: 'type',
|
||||||
|
info: {
|
||||||
|
field: 'type',
|
||||||
|
sortOrder: 0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,9 +13,19 @@
|
|||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
import {SCFacet, SCSearchFilter, SCSearchSort, SCThingType} from '@openstapps/core';
|
import {
|
||||||
|
SCFacet,
|
||||||
|
SCSearchFilter,
|
||||||
|
SCSearchSort,
|
||||||
|
SCThingTranslator,
|
||||||
|
SCThingType,
|
||||||
|
SCTranslations,
|
||||||
|
} from '@openstapps/core';
|
||||||
import {Subject} from 'rxjs';
|
import {Subject} from 'rxjs';
|
||||||
import {FilterBucket, FilterContext, FilterFacet, SortContext} from './context-type';
|
import {FilterBucket, FilterContext, FilterFacet, SortContext, TransformedFacet} from './context-type';
|
||||||
|
import {TranslateService} from '@ngx-translate/core';
|
||||||
|
import {ThingTranslateService} from '../../../translation/thing-translate.service';
|
||||||
|
import {transformFacets} from './facet-filter';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ContextMenuService provides bidirectional communication of context menu options and search queries
|
* ContextMenuService provides bidirectional communication of context menu options and search queries
|
||||||
@@ -72,6 +82,11 @@ export class ContextMenuService {
|
|||||||
*/
|
*/
|
||||||
sortQueryChanged$ = this.sortQuery.asObservable();
|
sortQueryChanged$ = this.sortQuery.asObservable();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly translate: TranslateService,
|
||||||
|
private readonly thingTranslate: ThingTranslateService,
|
||||||
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns SCSearchFilter if filterContext value is set, undefined otherwise
|
* Returns SCSearchFilter if filterContext value is set, undefined otherwise
|
||||||
*
|
*
|
||||||
@@ -178,18 +193,9 @@ export class ContextMenuService {
|
|||||||
* Updates the filter context options from given facets
|
* Updates the filter context options from given facets
|
||||||
*/
|
*/
|
||||||
updateContextFilter(facets: SCFacet[]) {
|
updateContextFilter(facets: SCFacet[]) {
|
||||||
// arrange facet field "type" to first position
|
const language = this.translate.currentLang as keyof SCTranslations<unknown>;
|
||||||
facets.sort((a: SCFacet, b: SCFacet) => {
|
const translator = new SCThingTranslator(language);
|
||||||
if (a.field === 'type') {
|
const transformedFacets = transformFacets(facets, language, this.thingTranslate, translator);
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (b.field === 'type') {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!this.contextFilter) {
|
if (!this.contextFilter) {
|
||||||
this.contextFilter = {
|
this.contextFilter = {
|
||||||
@@ -198,23 +204,24 @@ export class ContextMenuService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateContextFilterOptions(this.contextFilter, facets);
|
this.updateContextFilterOptions(this.contextFilter, transformedFacets);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates context filter with new facets.
|
* Updates context filter with new facets.
|
||||||
* It preserves the checked status of existing filter options
|
* It preserves the checked status of existing filter options
|
||||||
*/
|
*/
|
||||||
updateContextFilterOptions = (contextFilter: FilterContext, facets: SCFacet[]) => {
|
updateContextFilterOptions = (contextFilter: FilterContext, facets: TransformedFacet[]) => {
|
||||||
const newFilterOptions: FilterFacet[] = [];
|
const newFilterOptions: FilterFacet[] = [];
|
||||||
|
|
||||||
// iterate new facets
|
// iterate new facets
|
||||||
for (const facet of facets) {
|
for (const {facet, info} of facets) {
|
||||||
if (facet.buckets.length > 0) {
|
if (facet.buckets.length > 0) {
|
||||||
const newFilterFacet: FilterFacet = {
|
const newFilterFacet: FilterFacet = {
|
||||||
buckets: [],
|
buckets: [],
|
||||||
field: facet.field,
|
field: facet.field,
|
||||||
onlyOnType: facet.onlyOnType,
|
onlyOnType: facet.onlyOnType,
|
||||||
|
info,
|
||||||
};
|
};
|
||||||
newFilterOptions.push(newFilterFacet);
|
newFilterOptions.push(newFilterFacet);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020 StApps
|
* Copyright (C) 2023 StApps
|
||||||
* This program is free software: you can redistribute it and/or modify it
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
* under the terms of the GNU General Public License as published by the Free
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
* Software Foundation, version 3.
|
* Software Foundation, version 3.
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {SCFacet, SCFacetBucket} from '@openstapps/core';
|
import {SCFacet, SCFacetBucket, SCThingType} from '@openstapps/core';
|
||||||
|
|
||||||
export type ContextType = FilterContext | SortContext;
|
export type ContextType = FilterContext | SortContext;
|
||||||
|
|
||||||
@@ -84,6 +84,21 @@ export interface FilterFacet extends SCFacet {
|
|||||||
* Compact view of the option buckets
|
* Compact view of the option buckets
|
||||||
*/
|
*/
|
||||||
compact?: boolean;
|
compact?: boolean;
|
||||||
|
/**
|
||||||
|
* Translated information about the facet
|
||||||
|
*/
|
||||||
|
info: FacetInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FacetInfo {
|
||||||
|
onlyOnType?: SCThingType;
|
||||||
|
field: string;
|
||||||
|
sortOrder: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TransformedFacet {
|
||||||
|
facet: SCFacet;
|
||||||
|
info: FacetInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FilterBucket extends SCFacetBucket {
|
export interface FilterBucket extends SCFacetBucket {
|
||||||
|
|||||||
70
src/app/modules/menu/context/facet-filter.ts
Normal file
70
src/app/modules/menu/context/facet-filter.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 StApps
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {TransformedFacet} from './context-type';
|
||||||
|
import {SCFacet, SCThingTranslator, SCThingType, SCTranslations} from '@openstapps/core';
|
||||||
|
import {searchFilters} from '../../../../config/search-filter';
|
||||||
|
import {ThingTranslateService} from '../../../translation/thing-translate.service';
|
||||||
|
|
||||||
|
const filterConfig = Object.entries(searchFilters).map(([pattern, entries]) => {
|
||||||
|
return {
|
||||||
|
typePattern: new RegExp(`^${pattern}$`),
|
||||||
|
facetFilter: Object.entries(entries).map(([pattern, facet]) => ({
|
||||||
|
pattern: new RegExp(`^${pattern}$`),
|
||||||
|
...facet,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms facets to
|
||||||
|
*
|
||||||
|
* 1. only include facets that are allowed in the options
|
||||||
|
* 2. translates all fields
|
||||||
|
* 3. sorts the facets according to the config
|
||||||
|
*/
|
||||||
|
export function transformFacets(
|
||||||
|
facets: SCFacet[],
|
||||||
|
language: keyof SCTranslations<unknown>,
|
||||||
|
thingTranslate: ThingTranslateService,
|
||||||
|
translator: SCThingTranslator,
|
||||||
|
): TransformedFacet[] {
|
||||||
|
return facets
|
||||||
|
.map(facet => ({
|
||||||
|
facet,
|
||||||
|
info: filterConfig
|
||||||
|
.filter(({typePattern}) => typePattern.test((facet.onlyOnType as string) || ''))
|
||||||
|
.flatMap(({facetFilter}) =>
|
||||||
|
facetFilter
|
||||||
|
.filter(({pattern}) => pattern.test(facet.field))
|
||||||
|
.map(it => ({
|
||||||
|
onlyOnType: facet.onlyOnType
|
||||||
|
? (translator.translatedPropertyValue(facet.onlyOnType, 'type') as SCThingType)
|
||||||
|
: undefined,
|
||||||
|
field:
|
||||||
|
it.translations && it.name
|
||||||
|
? it.translations[language]?.name || it.name
|
||||||
|
: thingTranslate.getPropertyName(
|
||||||
|
facet.onlyOnType || SCThingType.AcademicEvent,
|
||||||
|
facet.field,
|
||||||
|
),
|
||||||
|
sortOrder: it.sortOrder,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
.sort(({sortOrder: a}, {sortOrder: b}) => a - b)[0],
|
||||||
|
}))
|
||||||
|
.filter(({info}) => !!info)
|
||||||
|
.sort(({info: {sortOrder: a}}, {info: {sortOrder: b}}) => a - b);
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Component, Input, OnDestroy, OnInit} from '@angular/core';
|
import {Component, Input, OnDestroy, OnInit} from '@angular/core';
|
||||||
import {SCSection} from './sections';
|
import {SCSection} from '../../../../config/profile-page-sections';
|
||||||
import {AuthHelperService} from '../../auth/auth-helper.service';
|
import {AuthHelperService} from '../../auth/auth-helper.service';
|
||||||
import {Observable, Subscription} from 'rxjs';
|
import {Observable, Subscription} from 'rxjs';
|
||||||
import {SCAuthorizationProviderType} from '@openstapps/core';
|
import {SCAuthorizationProviderType} from '@openstapps/core';
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import {ActivatedRoute} from '@angular/router';
|
|||||||
import {ScheduleProvider} from '../../calendar/schedule.provider';
|
import {ScheduleProvider} from '../../calendar/schedule.provider';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import {SCIcon} from '../../../util/ion-icon/icon';
|
import {SCIcon} from '../../../util/ion-icon/icon';
|
||||||
import {profilePageSections} from './sections';
|
import {profilePageSections} from '../../../../config/profile-page-sections';
|
||||||
import {filter, map} from 'rxjs/operators';
|
import {filter, map} from 'rxjs/operators';
|
||||||
|
|
||||||
const CourseCard = {
|
const CourseCard = {
|
||||||
|
|||||||
4
src/config/README.md
Normal file
4
src/config/README.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# Config files
|
||||||
|
|
||||||
|
This is a collection of config files which have been temporarily placed in the app,
|
||||||
|
but are designed or meant to be supplied by the backend as part of the config.
|
||||||
@@ -22,7 +22,7 @@ import {
|
|||||||
SCThingRemoteOrigin,
|
SCThingRemoteOrigin,
|
||||||
SCThingType,
|
SCThingType,
|
||||||
} from '@openstapps/core';
|
} from '@openstapps/core';
|
||||||
import {SCIcon} from '../../../util/ion-icon/icon';
|
import {SCIcon} from '../app/util/ion-icon/icon';
|
||||||
|
|
||||||
export const SCSectionThingType = 'section' as SCThingType;
|
export const SCSectionThingType = 'section' as SCThingType;
|
||||||
export const SCSectionLinkThingType = 'section link' as SCThingType;
|
export const SCSectionLinkThingType = 'section link' as SCThingType;
|
||||||
52
src/config/search-filter.ts
Normal file
52
src/config/search-filter.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 StApps
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {SCTranslations} from '@openstapps/core';
|
||||||
|
|
||||||
|
export type RegExpString = string;
|
||||||
|
export type SCSearchFilterConfig = Record<
|
||||||
|
RegExpString,
|
||||||
|
Record<
|
||||||
|
RegExpString,
|
||||||
|
{
|
||||||
|
name?: string;
|
||||||
|
sortOrder: number;
|
||||||
|
translations?: SCTranslations<{name: string}>;
|
||||||
|
}
|
||||||
|
>
|
||||||
|
>;
|
||||||
|
|
||||||
|
export const searchFilters: SCSearchFilterConfig = {
|
||||||
|
'.*': {
|
||||||
|
'type': {
|
||||||
|
sortOrder: 0,
|
||||||
|
},
|
||||||
|
'[^.]*': {
|
||||||
|
sortOrder: 10,
|
||||||
|
},
|
||||||
|
'academicTerms?\\.acronym': {
|
||||||
|
name: 'semester',
|
||||||
|
sortOrder: 2,
|
||||||
|
translations: {
|
||||||
|
de: {
|
||||||
|
name: 'Semester',
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
name: 'Semester',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -13,5 +13,6 @@
|
|||||||
"module": "es2020",
|
"module": "es2020",
|
||||||
"target": "es2017",
|
"target": "es2017",
|
||||||
"lib": ["es2020", "dom"]
|
"lib": ["es2020", "dom"]
|
||||||
}
|
},
|
||||||
|
"exclude": ["**/*.spec.ts"]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user