diff --git a/src/app/shared/components/filter-chips/filter-chips.component.ts b/src/app/shared/components/filter-chips/filter-chips.component.ts index 115944df9..5d356efb4 100644 --- a/src/app/shared/components/filter-chips/filter-chips.component.ts +++ b/src/app/shared/components/filter-chips/filter-chips.component.ts @@ -29,51 +29,16 @@ export class FilterChipsComponent { }); filterOptions = computed(() => { - // [RNi]: TODO check this with paging 5 for filter options and remove comment - - // return this.filters() - // .filter((filter) => filter.key && filter.options) - // .map((filter) => ({ - // key: filter.key, - // options: filter.options!.map((opt) => ({ - // id: String(opt.value || ''), - // value: String(opt.value || ''), - // label: opt.label, - // })), - // })); - - const filtersData = this.filters(); - const cachedOptions = this.filterOptionsCache(); - const options: Record = {}; - - filtersData.forEach((filter) => { - if (filter.key && filter.options) { - options[filter.key] = filter.options.map((opt) => ({ + return this.filters() + .filter((filter) => filter.key && filter.options) + .map((filter) => ({ + key: filter.key, + options: filter.options!.map((opt) => ({ id: String(opt.value || ''), value: String(opt.value || ''), label: opt.label, - })); - } - }); - - Object.entries(cachedOptions).forEach(([filterKey, cachedOpts]) => { - if (cachedOpts && cachedOpts.length > 0) { - const existingOptions = options[filterKey] || []; - const existingValues = new Set(existingOptions.map((opt) => opt.value)); - - const newCachedOptions = cachedOpts - .filter((opt) => !existingValues.has(String(opt.value || ''))) - .map((opt) => ({ - id: String(opt.value || ''), - value: String(opt.value || ''), - label: opt.label, - })); - - options[filterKey] = [...newCachedOptions, ...existingOptions]; - } - }); - - return options; + })), + })); }); chips = computed(() => { @@ -85,8 +50,7 @@ export class FilterChipsComponent { .filter(([, value]) => value !== null && value !== '') .map(([key, value]) => { const filterLabel = labels.find((l) => l.key === key)?.label || key; - //const filterOptionsList = options.find((o) => o.key === key)?.options || []; - const filterOptionsList = options[key] || []; + const filterOptionsList = options.find((o) => o.key === key)?.options || []; const option = filterOptionsList.find((opt) => opt.value === value || opt.id === value); const displayValue = option?.label || value || ''; diff --git a/src/app/shared/components/generic-filter/generic-filter.component.html b/src/app/shared/components/generic-filter/generic-filter.component.html index ac27514e4..28d77b529 100644 --- a/src/app/shared/components/generic-filter/generic-filter.component.html +++ b/src/app/shared/components/generic-filter/generic-filter.component.html @@ -1,7 +1,7 @@ diff --git a/src/app/shared/components/generic-filter/generic-filter.component.ts b/src/app/shared/components/generic-filter/generic-filter.component.ts index 62dd9eeba..694802fcb 100644 --- a/src/app/shared/components/generic-filter/generic-filter.component.ts +++ b/src/app/shared/components/generic-filter/generic-filter.component.ts @@ -16,7 +16,7 @@ import { import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { FormsModule } from '@angular/forms'; -import { SelectOption } from '@shared/models'; +import { FilterOption } from '@shared/models'; import { LoadingSpinnerComponent } from '../loading-spinner/loading-spinner.component'; @@ -29,8 +29,8 @@ import { LoadingSpinnerComponent } from '../loading-spinner/loading-spinner.comp }) export class GenericFilterComponent { private destroyRef = inject(DestroyRef); - options = input([]); - searchResults = input([]); + options = input([]); + searchResults = input([]); isLoading = input(false); isPaginationLoading = input(false); isSearchLoading = input(false); @@ -42,12 +42,12 @@ export class GenericFilterComponent { searchTextChanged = output(); loadMoreOptions = output(); - currentSelectedOption = signal(null); + currentSelectedOption = signal(null); private searchSubject = new Subject(); private currentSearchText = signal(''); - private searchResultOptions = signal([]); + private searchResultOptions = signal([]); private isActivelySearching = signal(false); - private stableOptionsArray: SelectOption[] = []; + private stableOptionsArray: FilterOption[] = []; filterOptions = computed(() => { const searchResults = this.searchResultOptions(); @@ -59,7 +59,7 @@ export class GenericFilterComponent { } const baseOptions = this.formatOptions(parentOptions); - let newOptions: SelectOption[]; + let newOptions: FilterOption[]; if (searchResults.length > 0) { const searchFormatted = this.formatOptions(searchResults); @@ -74,13 +74,13 @@ export class GenericFilterComponent { return this.stableOptionsArray; }); - private formatOptions(options: SelectOption[]): SelectOption[] { + private formatOptions(options: FilterOption[]): FilterOption[] { if (options.length > 0) { if (this.filterType() === 'dateCreated') { return options .filter((option) => option?.label) - .sort((a, b) => b.label.localeCompare(a.label)) .map((option) => ({ + ...option, label: option.label || '', value: option.label || '', })); @@ -89,6 +89,7 @@ export class GenericFilterComponent { .filter((option) => option?.label) .sort((a, b) => a.label.localeCompare(b.label)) .map((option) => ({ + ...option, label: option.label || '', value: option.value || '', })); @@ -97,7 +98,7 @@ export class GenericFilterComponent { return []; } - private arraysEqual(a: SelectOption[], b: SelectOption[]): boolean { + private arraysEqual(a: FilterOption[], b: FilterOption[]): boolean { if (a.length !== b.length) return false; for (let i = 0; i < a.length; i++) { if (a[i].value !== b[i].value || a[i].label !== b[i].label) { @@ -107,7 +108,7 @@ export class GenericFilterComponent { return true; } - private updateStableArray(newOptions: SelectOption[]): void { + private updateStableArray(newOptions: FilterOption[]): void { if (this.arraysEqual(this.stableOptionsArray, newOptions)) { return; } @@ -163,10 +164,6 @@ export class GenericFilterComponent { } } - trackByOption(index: number, option: SelectOption): string { - return option.value?.toString() || index.toString(); - } - onValueChange(event: SelectChangeEvent): void { const options = this.filterOptions(); const selectedOption = event.value ? options.find((opt) => opt.value === event.value) : null; diff --git a/src/app/shared/components/global-search/global-search.component.html b/src/app/shared/components/global-search/global-search.component.html index f2a841daf..fb83ead46 100644 --- a/src/app/shared/components/global-search/global-search.component.html +++ b/src/app/shared/components/global-search/global-search.component.html @@ -11,6 +11,7 @@ {{ 'resourceCard.labels.funder' | translate }} @for (funder of nodeFunders.slice(0, limit); track $index) { - {{ funder.name }}{{ $last ? '' : ', ' }} + {{ funder.name }}{{ $last ? '' : ', ' }} } @if (nodeFunders.length > limit) { {{ 'resourceCard.andCountMore' | translate: { count: nodeFunders.length - limit } }} @@ -26,14 +28,18 @@ @if (nodeLicense) {

{{ 'resourceCard.labels.license' | translate }} - {{ nodeLicense!.name }} + {{ + nodeLicense!.name + }}

} @if (resourceValue.absoluteUrl) {

{{ 'resourceCard.labels.url' | translate }} - {{ resourceValue.absoluteUrl }} + {{ + resourceValue.absoluteUrl + }}

} @@ -41,7 +47,7 @@

{{ 'resourceCard.labels.doi' | translate }} @for (doi of resourceValue.doi.slice(0, limit); track $index) { - {{ doi }}{{ $last ? '' : ', ' }} + {{ doi }}{{ $last ? '' : ', ' }} } @if (resourceValue.doi.length > limit) { {{ 'resourceCard.andCountMore' | translate: { count: resourceValue.doi.length - limit } }} diff --git a/src/app/shared/components/resource-card/components/preprint-secondary-metadata/preprint-secondary-metadata.component.html b/src/app/shared/components/resource-card/components/preprint-secondary-metadata/preprint-secondary-metadata.component.html index 6e0dc23e3..fb621f2ba 100644 --- a/src/app/shared/components/resource-card/components/preprint-secondary-metadata/preprint-secondary-metadata.component.html +++ b/src/app/shared/components/resource-card/components/preprint-secondary-metadata/preprint-secondary-metadata.component.html @@ -7,14 +7,16 @@ @if (resourceValue.provider) {

{{ 'resourceCard.labels.provider' | translate }} - {{ resourceValue.provider!.name }} + {{ + resourceValue.provider!.name + }}

} @if (resourceValue.hasDataResource) {

{{ 'resourceCard.labels.associatedData' | translate }} - + {{ resourceValue.hasDataResource }}

@@ -23,7 +25,12 @@ @if (resourceValue.hasPreregisteredAnalysisPlan) {

{{ 'resourceCard.labels.associatedAnalysisPlan' | translate }} - + {{ resourceValue.hasPreregisteredAnalysisPlan }}

@@ -32,7 +39,7 @@ @if (resourceValue.hasPreregisteredStudyDesign) {

{{ 'resourceCard.labels.associatedStudyDesign' | translate }} - + {{ resourceValue.hasPreregisteredStudyDesign }}

@@ -53,16 +60,22 @@ @if (resourceValue.license?.absoluteUrl) {

{{ 'resourceCard.labels.license' | translate }} - {{ - resourceValue.license!.name - }} + {{ resourceValue.license!.name }}

} @if (resourceValue.absoluteUrl) {

{{ 'resourceCard.labels.url' | translate }} - {{ resourceValue.absoluteUrl }} + {{ + resourceValue.absoluteUrl + }}

} @@ -71,7 +84,7 @@

{{ 'resourceCard.labels.doi' | translate }} @for (doi of resourceValue.doi.slice(0, limit); track $index) { - {{ doi }}{{ $last ? '' : ', ' }} + {{ doi }}{{ $last ? '' : ', ' }} } @if (resourceValue.doi.length > limit) { {{ 'resourceCard.andCountMore' | translate: { count: resourceValue.doi.length - limit } }} diff --git a/src/app/shared/components/resource-card/components/project-secondary-metadata/project-secondary-metadata.component.html b/src/app/shared/components/resource-card/components/project-secondary-metadata/project-secondary-metadata.component.html index 17e0c25d9..4184dfcb7 100644 --- a/src/app/shared/components/resource-card/components/project-secondary-metadata/project-secondary-metadata.component.html +++ b/src/app/shared/components/resource-card/components/project-secondary-metadata/project-secondary-metadata.component.html @@ -8,7 +8,9 @@

{{ 'resourceCard.labels.funder' | translate }} @for (funder of resourceValue.funders.slice(0, limit); track $index) { - {{ funder.name }}{{ $last ? '' : ', ' }} + {{ funder.name }}{{ $last ? '' : ', ' }} } @if (resourceValue.funders.length > limit) { {{ 'resourceCard.andCountMore' | translate: { count: resourceValue.funders.length - limit } }} @@ -23,7 +25,12 @@ @if (resourceValue.isPartOfCollection) {

{{ 'resourceCard.labels.collection' | translate }} - + {{ resourceValue.isPartOfCollection!.name }}

@@ -36,16 +43,22 @@ @if (resourceValue.license) {

{{ 'resourceCard.labels.license' | translate }} - {{ - resourceValue.license!.name - }} + {{ resourceValue.license!.name }}

} @if (resourceValue.absoluteUrl) {

{{ 'resourceCard.labels.url' | translate }} - {{ resourceValue.absoluteUrl }} + {{ + resourceValue.absoluteUrl + }}

} @@ -53,7 +66,7 @@

{{ 'resourceCard.labels.doi' | translate }} @for (doi of resourceValue.doi.slice(0, limit); track $index) { - {{ doi }}{{ $last ? '' : ', ' }} + {{ doi }}{{ $last ? '' : ', ' }} } @if (resourceValue.doi.length > limit) { {{ 'resourceCard.andCountMore' | translate: { count: resourceValue.doi.length - limit } }} diff --git a/src/app/shared/components/resource-card/components/registration-secondary-metadata/registration-secondary-metadata.component.html b/src/app/shared/components/resource-card/components/registration-secondary-metadata/registration-secondary-metadata.component.html index 84c00739c..68fdcf9ce 100644 --- a/src/app/shared/components/resource-card/components/registration-secondary-metadata/registration-secondary-metadata.component.html +++ b/src/app/shared/components/resource-card/components/registration-secondary-metadata/registration-secondary-metadata.component.html @@ -8,7 +8,9 @@

{{ 'resourceCard.labels.funder' | translate }} @for (funder of resourceValue.funders.slice(0, limit); track $index) { - {{ funder.name }}{{ $last ? '' : ', ' }} + {{ funder.name }}{{ $last ? '' : ', ' }} } @if (resourceValue.funders.length > limit) { {{ 'resourceCard.andCountMore' | translate: { count: resourceValue.funders.length - limit } }} @@ -19,7 +21,9 @@ @if (resourceValue.provider) {

{{ 'resourceCard.labels.provider' | translate }} - {{ resourceValue.provider!.name }} + {{ + resourceValue.provider!.name + }}

} @@ -30,14 +34,22 @@ @if (resourceValue.license) {

{{ 'resourceCard.labels.license' | translate }} - {{ resourceValue.license!.name }} + {{ resourceValue.license!.name }}

} @if (resourceValue.absoluteUrl) {

{{ 'resourceCard.labels.url' | translate }} - {{ resourceValue.absoluteUrl }} + {{ + resourceValue.absoluteUrl + }}

} @@ -46,7 +58,7 @@

{{ 'resourceCard.labels.doi' | translate }} @for (doi of resourceValue.doi.slice(0, limit); track $index) { - {{ doi }}{{ $last ? '' : ', ' }} + {{ doi }}{{ $last ? '' : ', ' }} } @if (resourceValue.doi.length > limit) { {{ 'resourceCard.andCountMore' | translate: { count: resourceValue.doi.length - limit } }} diff --git a/src/app/shared/components/resource-card/resource-card.component.html b/src/app/shared/components/resource-card/resource-card.component.html index b64db618f..6185e94ed 100644 --- a/src/app/shared/components/resource-card/resource-card.component.html +++ b/src/app/shared/components/resource-card/resource-card.component.html @@ -7,14 +7,14 @@

- {{ displayTitle() }} + {{ displayTitle() }}

@if (isWithdrawn()) { {{ 'resourceCard.labels.withdrawn' | translate }} } @let orcidValues = orcids(); @if (orcidValues.length && orcidValues[0]) { - + orcid } @@ -24,7 +24,7 @@

@if (affiliatedEntities().length > 0) {
@for (affiliatedEntity of affiliatedEntities().slice(0, limit); track $index) { - {{ affiliatedEntity.name }}{{ $last ? '' : ', ' }} } @@ -39,14 +39,18 @@

@if (resource().isPartOf) {

{{ 'resourceCard.labels.from' | translate }}

- {{ resource().isPartOf!.name }} + {{ + resource().isPartOf!.name + }}
} @if (resource().isContainedBy) {

{{ 'resourceCard.labels.from' | translate }}

- {{ resource().isContainedBy!.name }} + {{ + resource().isContainedBy!.name + }}
} diff --git a/src/app/shared/components/reusable-filter/reusable-filter.component.html b/src/app/shared/components/reusable-filter/reusable-filter.component.html index b79a44444..5cb3ca3fa 100644 --- a/src/app/shared/components/reusable-filter/reusable-filter.component.html +++ b/src/app/shared/components/reusable-filter/reusable-filter.component.html @@ -12,19 +12,19 @@ {{ getFilterLabel(filter) }} - @if (getFilterDescription(filter)) { -

{{ getFilterDescription(filter) }}

+ @if (filter.description) { +

{{ filter.description }}

} - @if (getFilterHelpLink(filter) && getFilterHelpLinkText(filter)) { + @if (filter.helpLink && filter.helpLinkText) {

- {{ getFilterHelpLinkText(filter) }} + {{ filter.helpLinkText }}

} @@ -33,9 +33,9 @@ -

}

diff --git a/src/app/shared/components/reusable-filter/reusable-filter.component.ts b/src/app/shared/components/reusable-filter/reusable-filter.component.ts index 80510a3c0..7fe28f27c 100644 --- a/src/app/shared/components/reusable-filter/reusable-filter.component.ts +++ b/src/app/shared/components/reusable-filter/reusable-filter.component.ts @@ -6,11 +6,11 @@ import { Checkbox, CheckboxChangeEvent } from 'primeng/checkbox'; import { NgClass } from '@angular/common'; import { ChangeDetectionStrategy, Component, computed, input, output, signal } from '@angular/core'; -import { ReactiveFormsModule } from '@angular/forms'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FILTER_PLACEHOLDERS } from '@osf/shared/constants'; import { StringOrNull } from '@osf/shared/helpers'; -import { DiscoverableFilter, SelectOption } from '@osf/shared/models'; +import { DiscoverableFilter, FilterOption } from '@osf/shared/models'; import { GenericFilterComponent } from '../generic-filter/generic-filter.component'; import { LoadingSpinnerComponent } from '../loading-spinner/loading-spinner.component'; @@ -29,6 +29,7 @@ import { LoadingSpinnerComponent } from '../loading-spinner/loading-spinner.comp LoadingSpinnerComponent, Checkbox, NgClass, + FormsModule, ], templateUrl: './reusable-filter.component.html', styleUrls: ['./reusable-filter.component.scss'], @@ -37,11 +38,13 @@ import { LoadingSpinnerComponent } from '../loading-spinner/loading-spinner.comp export class ReusableFilterComponent { filters = input([]); selectedValues = input>({}); - filterSearchResults = input>({}); + filterSearchResults = input>({}); isLoading = input(false); showEmptyState = input(true); plainStyle = input(false); + readonly Boolean = Boolean; + loadFilterOptions = output(); filterValueChanged = output<{ filterType: string; value: StringOrNull }>(); filterSearchChanged = output<{ filterType: string; searchText: string; filter: DiscoverableFilter }>(); @@ -152,11 +155,11 @@ export class ReusableFilterComponent { } } - getFilterOptions(filter: DiscoverableFilter): SelectOption[] { + getFilterOptions(filter: DiscoverableFilter): FilterOption[] { return filter.options || []; } - getFilterSearchResults(filter: DiscoverableFilter): SelectOption[] { + getFilterSearchResults(filter: DiscoverableFilter): FilterOption[] { const searchResults = this.filterSearchResults(); return searchResults[filter.key] || []; } @@ -165,14 +168,6 @@ export class ReusableFilterComponent { return filter.isLoading || false; } - isFilterPaginationLoading(filter: DiscoverableFilter): boolean { - return filter.isPaginationLoading || false; - } - - isFilterSearchLoading(filter: DiscoverableFilter): boolean { - return filter.isSearchLoading || false; - } - getSelectedValue(filterKey: string): string | null { return this.selectedValues()[filterKey] || null; } @@ -181,18 +176,6 @@ export class ReusableFilterComponent { return this.FILTER_PLACEHOLDERS[filterKey] || ''; } - getFilterDescription(filter: DiscoverableFilter): string | null { - return filter.description || null; - } - - getFilterHelpLink(filter: DiscoverableFilter): string | null { - return filter.helpLink || null; - } - - getFilterHelpLinkText(filter: DiscoverableFilter): string | null { - return filter.helpLinkText || ''; - } - getFilterLabel(filter: DiscoverableFilter): string { return filter.label || filter.key || ''; } @@ -217,9 +200,4 @@ export class ReusableFilterComponent { const isChecked = event?.checked || false; this.onIsPresentFilterToggle(filter, isChecked); } - - isIsPresentFilterChecked(filterKey: string): boolean { - const selectedValue = this.selectedValues()[filterKey]; - return selectedValue === 'true' || Boolean(selectedValue); - } } diff --git a/src/app/shared/components/search-results-container/search-results-container.component.ts b/src/app/shared/components/search-results-container/search-results-container.component.ts index 7aea4d1b4..2c1b4af7f 100644 --- a/src/app/shared/components/search-results-container/search-results-container.component.ts +++ b/src/app/shared/components/search-results-container/search-results-container.component.ts @@ -20,7 +20,7 @@ import { FormsModule } from '@angular/forms'; import { PreprintProviderDetails } from '@osf/features/preprints/models'; import { searchSortingOptions } from '@osf/shared/constants'; import { ResourceType } from '@osf/shared/enums'; -import { ResourceModel, TabOption } from '@osf/shared/models'; +import { DiscoverableFilter, ResourceModel, TabOption } from '@osf/shared/models'; import { LoadingSpinnerComponent } from '../loading-spinner/loading-spinner.component'; import { ResourceCardComponent } from '../resource-card/resource-card.component'; @@ -48,6 +48,7 @@ import { SelectComponent } from '../select/select.component'; export class SearchResultsContainerComponent { resources = input([]); areResourcesLoading = input(false); + filters = input([]); searchCount = input(0); selectedSort = input(''); selectedTab = input(ResourceType.Null); @@ -78,9 +79,9 @@ export class SearchResultsContainerComponent { }); readonly hasFilters = computed(() => { - //[RNi] TODO: check if there are any filters - return true; + return this.filters().length > 0; }); + filtersComponent = contentChild>('filtersComponent'); selectSort(value: string): void { diff --git a/src/app/shared/mappers/filters/filter-option.mapper.ts b/src/app/shared/mappers/filters/filter-option.mapper.ts index 0f62a61ec..9418fe108 100644 --- a/src/app/shared/mappers/filters/filter-option.mapper.ts +++ b/src/app/shared/mappers/filters/filter-option.mapper.ts @@ -1,29 +1,39 @@ -import { FilterOptionItem, SelectOption } from '@shared/models'; +import { FilterOption, FilterOptionItem, SearchResultJsonApi } from '@shared/models'; -export function mapFilterOption(item: FilterOptionItem): SelectOption { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const metadata: any = item.attributes.resourceMetadata; - const id = metadata['@id']; +export function mapFilterOptions( + searchResultItems: SearchResultJsonApi[], + filterOptionItems: FilterOptionItem[] +): FilterOption[] { + return searchResultItems.map((searchResult) => { + const cardSearchResultCount = searchResult.attributes!.cardSearchResultCount; + const filterOption = filterOptionItems.find((option) => option.id === searchResult.relationships.indexCard.data.id); + const filterOptionMetadata = filterOption?.attributes.resourceMetadata; + const id = filterOptionMetadata['@id']; - if ('title' in metadata) { - return { - label: metadata?.title?.[0]?.['@value'], - value: id, - }; - } else if ('displayLabel' in metadata) { - return { - label: metadata.displayLabel?.[0]?.['@value'], - value: id, - }; - } else if ('name' in metadata) { - return { - label: metadata.name?.[0]?.['@value'], - value: id, - }; - } else { - return { - label: '', - value: id, - }; - } + if ('title' in filterOptionMetadata) { + return { + label: filterOptionMetadata?.title?.[0]?.['@value'], + value: id, + cardSearchResultCount, + }; + } else if ('displayLabel' in filterOptionMetadata) { + return { + label: filterOptionMetadata.displayLabel?.[0]?.['@value'], + value: id, + cardSearchResultCount, + }; + } else if ('name' in filterOptionMetadata) { + return { + label: filterOptionMetadata.name?.[0]?.['@value'], + value: id, + cardSearchResultCount, + }; + } else { + return { + label: '', + value: id, + cardSearchResultCount, + }; + } + }); } diff --git a/src/app/shared/models/search/discaverable-filter.model.ts b/src/app/shared/models/search/discaverable-filter.model.ts index 80c57e034..3ffd12d64 100644 --- a/src/app/shared/models/search/discaverable-filter.model.ts +++ b/src/app/shared/models/search/discaverable-filter.model.ts @@ -5,8 +5,8 @@ export interface DiscoverableFilter { label: string; type: 'select' | 'date' | 'checkbox' | 'group'; operator: string; - options?: SelectOption[]; - selectedValues?: SelectOption[]; + options?: FilterOption[]; + selectedValues?: FilterOption[]; description?: string; helpLink?: string; helpLinkText?: string; @@ -19,3 +19,7 @@ export interface DiscoverableFilter { loadOptionsOnExpand?: boolean; filters?: DiscoverableFilter[]; } + +export interface FilterOption extends SelectOption { + cardSearchResultCount: number; +} diff --git a/src/app/shared/models/search/filter-options-json-api.models.ts b/src/app/shared/models/search/filter-options-json-api.models.ts index e10db93d5..00073f061 100644 --- a/src/app/shared/models/search/filter-options-json-api.models.ts +++ b/src/app/shared/models/search/filter-options-json-api.models.ts @@ -1,8 +1,10 @@ +import { SearchResultJsonApi } from '@shared/models'; + import { ApiData } from '../common'; export interface FilterOptionsResponseJsonApi { data: FilterOptionsResponseData; - included?: FilterOptionItem[]; + included?: (FilterOptionItem | SearchResultJsonApi)[]; links?: { first?: string; next?: string; diff --git a/src/app/shared/models/search/index-card-search-json-api.models.ts b/src/app/shared/models/search/index-card-search-json-api.models.ts index 8ca55bb5a..5f4c3154e 100644 --- a/src/app/shared/models/search/index-card-search-json-api.models.ts +++ b/src/app/shared/models/search/index-card-search-json-api.models.ts @@ -40,6 +40,9 @@ export interface SearchResultJsonApi { }; }; }; + attributes?: { + cardSearchResultCount: number; + }; } export type IndexCardDataJsonApi = ApiData; diff --git a/src/app/shared/services/global-search.service.ts b/src/app/shared/services/global-search.service.ts index 266071caa..74c5434aa 100644 --- a/src/app/shared/services/global-search.service.ts +++ b/src/app/shared/services/global-search.service.ts @@ -5,16 +5,16 @@ import { inject, Injectable } from '@angular/core'; import { JsonApiService } from '@osf/shared/services'; import { MapResources } from '@shared/mappers/search'; import { + FilterOption, FilterOptionItem, FilterOptionsResponseJsonApi, IndexCardDataJsonApi, IndexCardSearchResponseJsonApi, ResourcesData, SearchResultJsonApi, - SelectOption, } from '@shared/models'; -import { AppliedFilter, CombinedFilterMapper, mapFilterOption, RelatedPropertyPathItem } from '../mappers'; +import { AppliedFilter, CombinedFilterMapper, mapFilterOptions, RelatedPropertyPathItem } from '../mappers'; import { environment } from 'src/environments/environment'; @@ -36,32 +36,31 @@ export class GlobalSearchService { .pipe(map((response) => this.handleResourcesRawResponse(response))); } - getFilterOptions(params: Record): Observable<{ options: SelectOption[]; nextUrl?: string }> { + getFilterOptions(params: Record): Observable<{ options: FilterOption[]; nextUrl?: string }> { return this.jsonApiService .get(`${environment.shareTroveUrl}/index-value-search`, params) .pipe(map((response) => this.handleFilterOptionsRawResponse(response))); } - getFilterOptionsFromPaginationUrl(url: string): Observable<{ options: SelectOption[]; nextUrl?: string }> { + getFilterOptionsFromPaginationUrl(url: string): Observable<{ options: FilterOption[]; nextUrl?: string }> { return this.jsonApiService .get(url) .pipe(map((response) => this.handleFilterOptionsRawResponse(response))); } private handleFilterOptionsRawResponse(response: FilterOptionsResponseJsonApi): { - options: SelectOption[]; + options: FilterOption[]; nextUrl?: string; } { - const options: SelectOption[] = []; + const options: FilterOption[] = []; let nextUrl: string | undefined; - if (response?.included) { - const filterOptionItems = response.included.filter( - (item): item is FilterOptionItem => item.type === 'index-card' && !!item.attributes?.resourceMetadata - ); + const searchResultItems = response + .included!.filter((item): item is SearchResultJsonApi => item.type === 'search-result') + .sort((a, b) => Number(a.id.at(-1)) - Number(b.id.at(-1))); + const filterOptionItems = response.included!.filter((item): item is FilterOptionItem => item.type === 'index-card'); - options.push(...filterOptionItems.map((item) => mapFilterOption(item))); - } + options.push(...mapFilterOptions(searchResultItems, filterOptionItems)); const searchResultPage = response?.data?.relationships?.['searchResultPage'] as { links?: { next?: { href: string } }; diff --git a/src/app/shared/stores/global-search/global-search.model.ts b/src/app/shared/stores/global-search/global-search.model.ts index af1404f75..98de2e0b3 100644 --- a/src/app/shared/stores/global-search/global-search.model.ts +++ b/src/app/shared/stores/global-search/global-search.model.ts @@ -1,14 +1,14 @@ import { ResourceType } from '@osf/shared/enums'; import { StringOrNull } from '@osf/shared/helpers'; -import { AsyncStateModel, DiscoverableFilter, ResourceModel, SelectOption } from '@osf/shared/models'; +import { AsyncStateModel, DiscoverableFilter, FilterOption, ResourceModel } from '@osf/shared/models'; export interface GlobalSearchStateModel { resources: AsyncStateModel; filters: DiscoverableFilter[]; defaultFilterValues: Record; filterValues: Record; - filterOptionsCache: Record; - filterSearchCache: Record; + filterOptionsCache: Record; + filterSearchCache: Record; filterPaginationCache: Record; resourcesCount: number; searchText: StringOrNull; diff --git a/src/app/shared/stores/global-search/global-search.selectors.ts b/src/app/shared/stores/global-search/global-search.selectors.ts index d9bb6cba2..ec60a0742 100644 --- a/src/app/shared/stores/global-search/global-search.selectors.ts +++ b/src/app/shared/stores/global-search/global-search.selectors.ts @@ -2,7 +2,7 @@ import { Selector } from '@ngxs/store'; import { ResourceType } from '@osf/shared/enums'; import { StringOrNull } from '@osf/shared/helpers'; -import { DiscoverableFilter, ResourceModel, SelectOption } from '@osf/shared/models'; +import { DiscoverableFilter, FilterOption, ResourceModel } from '@osf/shared/models'; import { GlobalSearchStateModel } from './global-search.model'; import { GlobalSearchState } from './global-search.state'; @@ -69,12 +69,12 @@ export class GlobalSearchSelectors { } @Selector([GlobalSearchState]) - static getFilterOptionsCache(state: GlobalSearchStateModel): Record { + static getFilterOptionsCache(state: GlobalSearchStateModel): Record { return state.filterOptionsCache; } @Selector([GlobalSearchState]) - static getFilterSearchCache(state: GlobalSearchStateModel): Record { + static getFilterSearchCache(state: GlobalSearchStateModel): Record { return state.filterSearchCache; } diff --git a/src/app/shared/stores/global-search/global-search.state.ts b/src/app/shared/stores/global-search/global-search.state.ts index 034bba50e..82342dffe 100644 --- a/src/app/shared/stores/global-search/global-search.state.ts +++ b/src/app/shared/stores/global-search/global-search.state.ts @@ -141,9 +141,9 @@ export class GlobalSearchState { @Action(LoadFilterOptionsWithSearch) loadFilterOptionsWithSearch(ctx: StateContext, action: LoadFilterOptionsWithSearch) { const state = ctx.getState(); + const filterKey = action.filterKey; const loadingFilters = state.filters.map((f) => (f.key === filterKey ? { ...f, isSearchLoading: true } : f)); ctx.patchState({ filters: loadingFilters }); - const filterKey = action.filterKey; return this.searchService .getFilterOptions(this.buildParamsForIndexValueSearch(state, filterKey, action.searchText)) .pipe( @@ -288,7 +288,7 @@ export class GlobalSearchState { ): Record { return { ...this.buildParamsForIndexCardSearch(state), - 'page[size]': '50', + 'page[size]': '200', valueSearchPropertyPath: filterKey, valueSearchText: valueSearchText ?? '', };