diff --git a/frontend/app/src/app/modules/data/data.module.ts b/frontend/app/src/app/modules/data/data.module.ts
index 09697966..56ba82f4 100644
--- a/frontend/app/src/app/modules/data/data.module.ts
+++ b/frontend/app/src/app/modules/data/data.module.ts
@@ -107,6 +107,7 @@ import {SemesterListItemComponent} from './types/semester/semester-list-item.com
import {VideoDetailContentComponent} from './types/video/video-detail-content.component';
import {VideoListItemComponent} from './types/video/video-list-item.component';
import {ShareButtonComponent} from './elements/share-button.component';
+import {DataFilterComponent} from './filter/data-filter.component';
/**
* Module for handling data
@@ -184,6 +185,7 @@ import {ShareButtonComponent} from './elements/share-button.component';
ShareButtonComponent,
],
imports: [
+ DataFilterComponent,
CommonModule,
DataRoutingModule,
FormsModule,
diff --git a/frontend/app/src/app/modules/data/filter/data-filter.component.ts b/frontend/app/src/app/modules/data/filter/data-filter.component.ts
new file mode 100644
index 00000000..f591076a
--- /dev/null
+++ b/frontend/app/src/app/modules/data/filter/data-filter.component.ts
@@ -0,0 +1,28 @@
+import {AsyncPipe, CommonModule} from '@angular/common';
+import {ChangeDetectionStrategy, Component, Input} from '@angular/core';
+import {IonicModule} from '@ionic/angular';
+import {IonIconModule} from 'src/app/util/ion-icon/ion-icon.module';
+import {DataFilterProvider} from './data-filter.provider';
+import {ThingTranslateModule} from 'src/app/translation/thing-translate.module';
+import {PropertyValueTranslatePipe} from 'src/app/translation/property-value-translate.pipe';
+
+@Component({
+ selector: 'stapps-data-filter',
+ templateUrl: 'data-filter.html',
+ styleUrls: ['data-filter.scss'],
+ standalone: true,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ imports: [
+ CommonModule,
+ IonicModule,
+ IonIconModule,
+ AsyncPipe,
+ ThingTranslateModule,
+ PropertyValueTranslatePipe,
+ ],
+})
+export class DataFilterComponent {
+ @Input() expandBucketCount = 3;
+
+ constructor(readonly filterProvider: DataFilterProvider) {}
+}
diff --git a/frontend/app/src/app/modules/data/filter/data-filter.html b/frontend/app/src/app/modules/data/filter/data-filter.html
new file mode 100644
index 00000000..f8d2b4a0
--- /dev/null
+++ b/frontend/app/src/app/modules/data/filter/data-filter.html
@@ -0,0 +1,75 @@
+
+ Relevance
+
+
+
+
+
+
+ Relevance
+ Name
+ Type
+
+
+
+
+
+
+
+ 1">
+
+
+ {{facet.onlyOnType | propertyValueTranslate: 'type': facet.onlyOnType | titlecase}}
+ / {{facet.field | propertyNameTranslate: facet.onlyOnType | titlecase}}
+
+
+ {{facet.field | propertyNameTranslate: $any('building') | titlecase}}
+
+
+
+ {{bucket.key | propertyValueTranslate: facet.field: (facet.onlyOnType ?? $any(bucket.key)) |
+ titlecase}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{bucket.key}}
+ {{bucket.count}}
+
+
+
+
+
+
+
+
+
diff --git a/frontend/app/src/app/modules/data/filter/data-filter.provider.ts b/frontend/app/src/app/modules/data/filter/data-filter.provider.ts
new file mode 100644
index 00000000..ba133826
--- /dev/null
+++ b/frontend/app/src/app/modules/data/filter/data-filter.provider.ts
@@ -0,0 +1,12 @@
+import {Injectable} from '@angular/core';
+import {SCSearchResult} from '@openstapps/core';
+import {BehaviorSubject} from 'rxjs';
+
+@Injectable()
+export class DataFilterProvider {
+ readonly context = new BehaviorSubject(undefined);
+
+ readonly userSortOption = new BehaviorSubject(undefined);
+
+ readonly userFilterOption = new BehaviorSubject(new Map());
+}
diff --git a/frontend/app/src/app/modules/data/filter/data-filter.scss b/frontend/app/src/app/modules/data/filter/data-filter.scss
new file mode 100644
index 00000000..c9895d31
--- /dev/null
+++ b/frontend/app/src/app/modules/data/filter/data-filter.scss
@@ -0,0 +1,36 @@
+:host {
+ overflow-x: auto;
+ display: flex;
+ align-items: flex-start;
+ margin-block: var(--spacing-xs);
+
+ > * {
+ flex-shrink: 0;
+ }
+}
+
+.expanded-facet {
+ display: flex;
+ flex-direction: column;
+
+ > .facet-label {
+ margin-inline: var(--spacing-md);
+ font-size: 0.7em;
+ }
+}
+
+.separator {
+ align-self: center;
+
+ width: 1px;
+ height: 1.25em;
+ margin-inline: var(--spacing-xs);
+
+ opacity: 0.2;
+ background: currentcolor;
+
+ &:last-child,
+ + .separator {
+ display: none;
+ }
+}
diff --git a/frontend/app/src/app/modules/data/list/search-page.component.ts b/frontend/app/src/app/modules/data/list/search-page.component.ts
index 90ff6d8b..4c164ddb 100644
--- a/frontend/app/src/app/modules/data/list/search-page.component.ts
+++ b/frontend/app/src/app/modules/data/list/search-page.component.ts
@@ -12,7 +12,7 @@
* You should have received a copy of the GNU General Public License along with
* this program. If not, see .
*/
-import {Component, DestroyRef, inject, Input, OnInit} from '@angular/core';
+import {Component, DestroyRef, Inject, inject, Input, OnInit} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router';
import {Keyboard} from '@capacitor/keyboard';
import {AlertController, AnimationBuilder, AnimationController} from '@ionic/angular';
@@ -36,6 +36,7 @@ import {PositionService} from '../../map/position.service';
import {ConfigProvider} from '../../config/config.provider';
import {searchPageSwitchAnimation} from './search-page-switch-animation';
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
+import {DataFilterProvider} from '../filter/data-filter.provider';
/**
* SearchPageComponent queries things and shows list of things as search results and filter as context menu
@@ -44,7 +45,7 @@ import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
selector: 'stapps-search-page',
templateUrl: 'search-page.html',
styleUrls: ['search-page.scss'],
- providers: [ContextMenuService],
+ providers: [ContextMenuService, DataFilterProvider],
})
export class SearchPageComponent implements OnInit {
@Input() title = 'search.title';
@@ -144,6 +145,8 @@ export class SearchPageComponent implements OnInit {
destroy$ = inject(DestroyRef);
+ dataFilterProvider = inject(DataFilterProvider);
+
routeAnimation: AnimationBuilder;
/**
@@ -216,6 +219,7 @@ export class SearchPageComponent implements OnInit {
try {
const result = await this.dataProvider.search(searchOptions);
+ this.dataFilterProvider.context.next(result);
this.singleTypeResponse = result.facets.find(facet => facet.field === 'type')?.buckets.length === 1;
if (append) {
// append results
diff --git a/frontend/app/src/app/modules/data/list/search-page.html b/frontend/app/src/app/modules/data/list/search-page.html
index ee89ae42..e8fc7a92 100644
--- a/frontend/app/src/app/modules/data/list/search-page.html
+++ b/frontend/app/src/app/modules/data/list/search-page.html
@@ -16,62 +16,41 @@
@if (showDrawer && showTopToolbar) {
-
-
-
-
- {{ title | translate }}
-
+
+
+
+
+ {{ title | translate }}
+
}
-
+
@if (showNavigation && isHebisAvailable) {
-
-
- {{ 'search.type' | translate }}
- {{ 'hebisSearch.type' | translate }}
-
-
-
+
+
+ {{ 'search.type' | translate }}
+ {{ 'hebisSearch.type' | translate }}
+
+
+
}
-
+
{{ searchInstruction | translate }}
-
+
+
diff --git a/frontend/app/src/app/translation/property-value-translate.pipe.ts b/frontend/app/src/app/translation/property-value-translate.pipe.ts
new file mode 100644
index 00000000..0b9fe8fb
--- /dev/null
+++ b/frontend/app/src/app/translation/property-value-translate.pipe.ts
@@ -0,0 +1,19 @@
+import {Pipe, PipeTransform} from '@angular/core';
+import {ThingTranslateService} from './thing-translate.service';
+import {SCThingType, SCThings} from '@openstapps/core';
+
+@Pipe({
+ name: 'propertyValueTranslate',
+ standalone: true,
+})
+export class PropertyValueTranslatePipe implements PipeTransform {
+ constructor(private readonly thingTranslate: ThingTranslateService) {}
+
+ transform<
+ K extends string | undefined,
+ T extends SCThingType,
+ U extends keyof Extract
,
+ >(value: K, field: (U & string) | string, type: T): K {
+ return this.thingTranslate.getPropertyValue(type, field, value) as K;
+ }
+}
diff --git a/frontend/app/src/app/translation/thing-translate.service.ts b/frontend/app/src/app/translation/thing-translate.service.ts
index cf1122c9..7cbfcc84 100644
--- a/frontend/app/src/app/translation/thing-translate.service.ts
+++ b/frontend/app/src/app/translation/thing-translate.service.ts
@@ -113,4 +113,8 @@ export class ThingTranslateService {
return this.getParsedResult(translatedPropertyNames, keyPath);
}
+
+ public getPropertyValue(type: SCThingType, field: string, key: string | undefined): string | undefined {
+ return this.translator.translatedPropertyValue(type, field, key);
+ }
}