From d49575ba038ab96db6ba4bb020e260693fe7a5da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20Ja=C5=9Bkowski?= <138671284+g-jaskowski@users.noreply.github.com> Date: Wed, 17 Sep 2025 12:18:23 +0200 Subject: [PATCH] [MNT-24388] fix location search facet (#11171) * [MNT-24388] fetch sites for locations facet * [MNT-24388] fix sonar issues * [MNT-24388] apply cr comments --- ...ilter-autocomplete-chips.component.spec.ts | 28 +++++++++++ ...rch-filter-autocomplete-chips.component.ts | 48 ++++++++++++++++--- .../models/autocomplete-option.interface.ts | 3 +- 3 files changed, 71 insertions(+), 8 deletions(-) diff --git a/lib/content-services/src/lib/search/components/search-filter-autocomplete-chips/search-filter-autocomplete-chips.component.spec.ts b/lib/content-services/src/lib/search/components/search-filter-autocomplete-chips/search-filter-autocomplete-chips.component.spec.ts index c0ef0619ff..31726e1178 100644 --- a/lib/content-services/src/lib/search/components/search-filter-autocomplete-chips/search-filter-autocomplete-chips.component.spec.ts +++ b/lib/content-services/src/lib/search/components/search-filter-autocomplete-chips/search-filter-autocomplete-chips.component.spec.ts @@ -22,11 +22,14 @@ import { SearchFilterAutocompleteChipsComponent } from './search-filter-autocomp import { EMPTY, of, ReplaySubject } from 'rxjs'; import { AutocompleteField } from '../../models/autocomplete-option.interface'; import { TagService } from '../../../tag/services/tag.service'; +import { SitesService } from '../../../common/services/sites.service'; +import { SitePaging } from '@alfresco/js-api'; describe('SearchFilterAutocompleteChipsComponent', () => { let component: SearchFilterAutocompleteChipsComponent; let fixture: ComponentFixture; let tagService: TagService; + let sitesService: SitesService; beforeEach(() => { TestBed.configureTestingModule({ @@ -42,6 +45,7 @@ describe('SearchFilterAutocompleteChipsComponent', () => { fixture = TestBed.createComponent(SearchFilterAutocompleteChipsComponent); component = fixture.componentInstance; tagService = TestBed.inject(TagService); + sitesService = TestBed.inject(SitesService); component.id = 'test-id'; component.context = { queryFragments: { @@ -160,4 +164,28 @@ describe('SearchFilterAutocompleteChipsComponent', () => { expect(component.selectedOptions).toEqual([{ value: 'option2' }, { value: 'option1' }]); expect(component.context.filterLoaded.next).toHaveBeenCalled(); }); + + it('should populate LOCATION field options with sites and predefined values from config', (done) => { + const sitesMock: SitePaging = { + list: { + pagination: {}, + entries: [ + { entry: { guid: 'site1', id: 'site1', title: 'Marketing', visibility: 'public' } }, + { entry: { guid: 'site2', id: 'site2', title: 'Finance', visibility: 'private' } } + ] + } + }; + component.settings.field = AutocompleteField.LOCATION; + component.settings.autocompleteOptions = [{ value: 'Repository', query: `PATH:'somePath'` }]; + spyOn(sitesService, 'getSites').and.returnValue(of(sitesMock)); + component.onInputChange('mark'); + component.autocompleteOptions$.subscribe((result) => { + expect(result).toEqual([ + { id: 'site1', value: 'Marketing' }, + { id: 'site2', value: 'Finance' }, + { value: 'Repository', query: `PATH:'somePath'` } + ]); + done(); + }); + }); }); diff --git a/lib/content-services/src/lib/search/components/search-filter-autocomplete-chips/search-filter-autocomplete-chips.component.ts b/lib/content-services/src/lib/search/components/search-filter-autocomplete-chips/search-filter-autocomplete-chips.component.ts index d82b9c68cb..0d3909af0e 100644 --- a/lib/content-services/src/lib/search/components/search-filter-autocomplete-chips/search-filter-autocomplete-chips.component.ts +++ b/lib/content-services/src/lib/search/components/search-filter-autocomplete-chips/search-filter-autocomplete-chips.component.ts @@ -30,6 +30,7 @@ import { SearchChipAutocompleteInputComponent } from '../search-chip-autocomplet import { TranslatePipe } from '@ngx-translate/core'; import { MatButtonModule } from '@angular/material/button'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { SitesService } from '../../../common/services/sites.service'; @Component({ selector: 'adf-search-filter-autocomplete-chips', @@ -54,7 +55,11 @@ export class SearchFilterAutocompleteChipsComponent implements SearchWidget, OnI private readonly destroyRef = inject(DestroyRef); - constructor(private tagService: TagService, private categoryService: CategoryService) { + constructor( + private readonly tagService: TagService, + private readonly categoryService: CategoryService, + private readonly sitesService: SitesService + ) { this.options = new SearchFilterList(); } @@ -117,8 +122,12 @@ export class SearchFilterAutocompleteChipsComponent implements SearchWidget, OnI } onInputChange(value: string) { - if (this.settings.field === AutocompleteField.CATEGORIES && value) { - this.searchForExistingCategories(value); + if (value) { + if (this.settings.field === AutocompleteField.CATEGORIES) { + this.searchForExistingCategories(value); + } else if (this.settings.field === AutocompleteField.LOCATION) { + this.populateSitesOptions(); + } } } @@ -131,10 +140,16 @@ export class SearchFilterAutocompleteChipsComponent implements SearchWidget, OnI this.displayValue$.next(this.selectedOptions.map((option) => option.value).join(', ')); if (this.context && this.settings && this.settings.field) { let queryFragments; - if (this.settings.field === AutocompleteField.CATEGORIES) { - queryFragments = this.selectedOptions.map((val) => `${this.settings.field}:"workspace://SpacesStore/${val.id}"`); - } else { - queryFragments = this.selectedOptions.map((val) => val.query ?? `${this.settings.field}:"${val.value}"`); + switch (this.settings.field) { + case AutocompleteField.CATEGORIES: + queryFragments = this.selectedOptions.map((val) => `${this.settings.field}:"workspace://SpacesStore/${val.id}"`); + break; + case AutocompleteField.LOCATION: + queryFragments = this.selectedOptions.map((val) => val.query ?? `${this.settings.field}:"${val.id}"`); + break; + default: + queryFragments = this.selectedOptions.map((val) => val.query ?? `${this.settings.field}:"${val.value}"`); + break; } this.context.queryFragments[this.id] = queryFragments.join(' OR '); if (updateContext) { @@ -157,6 +172,9 @@ export class SearchFilterAutocompleteChipsComponent implements SearchWidget, OnI case AutocompleteField.CATEGORIES: this.autocompleteOptionsSubject$.next([]); break; + case AutocompleteField.LOCATION: + this.autocompleteOptionsSubject$.next([]); + break; default: this.autocompleteOptionsSubject$.next(this.settings.autocompleteOptions); } @@ -173,4 +191,20 @@ export class SearchFilterAutocompleteChipsComponent implements SearchWidget, OnI ); }); } + + private populateSitesOptions(): void { + this.sitesService + .getSites() + .pipe( + map((sites) => { + const predefinedOptions = this.settings?.autocompleteOptions || []; + const sitesOptions = sites.list.entries.map((siteEntry) => ({ + id: siteEntry.entry.id, + value: siteEntry.entry.title + })); + return [...sitesOptions, ...predefinedOptions]; + }) + ) + .subscribe((options) => this.autocompleteOptionsSubject$.next(options)); + } } diff --git a/lib/content-services/src/lib/search/models/autocomplete-option.interface.ts b/lib/content-services/src/lib/search/models/autocomplete-option.interface.ts index 2a4b9360c4..7918b47c3b 100644 --- a/lib/content-services/src/lib/search/models/autocomplete-option.interface.ts +++ b/lib/content-services/src/lib/search/models/autocomplete-option.interface.ts @@ -24,5 +24,6 @@ export interface AutocompleteOption { export enum AutocompleteField { TAG = 'TAG', - CATEGORIES = 'cm:categories' + CATEGORIES = 'cm:categories', + LOCATION = 'SITE' }