diff --git a/apps/metadata-editor-e2e/src/e2e/dashboard.cy.ts b/apps/metadata-editor-e2e/src/e2e/dashboard.cy.ts
index 13a9215b9..5b76e1b93 100644
--- a/apps/metadata-editor-e2e/src/e2e/dashboard.cy.ts
+++ b/apps/metadata-editor-e2e/src/e2e/dashboard.cy.ts
@@ -296,12 +296,26 @@ describe('dashboard (authenticated)', () => {
})
})
describe('search filters', () => {
- function checkFilterByChangeDate() {
+ function selectUser(index = 0, openDropdown = true) {
+ if (openDropdown) {
+ cy.get('md-editor-search-filters').find('gn-ui-button').first().click()
+ }
+ cy.get('.cdk-overlay-container')
+ .find('input[type="checkbox"]')
+ .eq(index)
+ .check()
+ }
+ function selectDateRange() {
cy.get('mat-calendar-header').find('button').first().click()
cy.get('mat-multi-year-view').contains('button', '2024').click()
cy.get('mat-year-view').contains('button', 'AUG').click()
cy.get('mat-month-view').contains('button', '1').click()
cy.get('mat-month-view').contains('button', '30').click()
+ }
+ function closeDropDown() {
+ cy.get('body').click(0, 0)
+ }
+ function checkFilterByChangeDate() {
cy.get('gn-ui-interactive-table')
.find('[data-cy="table-row"]')
.should('have.length', '1')
@@ -322,12 +336,8 @@ describe('dashboard (authenticated)', () => {
.find('gn-ui-button')
.should('have.length', 2)
})
- it('should filter the record list by editor (Barbara Roberts)', () => {
- cy.get('md-editor-search-filters').find('gn-ui-button').first().click()
- cy.get('.cdk-overlay-container')
- .find('input[type="checkbox"]')
- .eq(1)
- .check()
+ it('should filter the record list by user (Barbara Roberts)', () => {
+ selectUser(1)
cy.get('gn-ui-interactive-table')
.find('[data-cy="table-row"]')
.should('have.length', '5')
@@ -339,18 +349,19 @@ describe('dashboard (authenticated)', () => {
})
it('should filter the record list by last update (changeDate)', () => {
cy.get('md-editor-search-filters').find('gn-ui-button').eq(1).click()
+ selectDateRange()
checkFilterByChangeDate()
})
it('should display the expand icon for the date range dropdown correctly', () => {
cy.get('md-editor-search-filters')
.find('gn-ui-date-range-dropdown')
- .find('mat-icon')
- .should('contain.text', 'expand_more')
+ .find('ng-icon')
+ .should('have.attr', 'ng-reflect-name', 'matExpandMore')
cy.get('md-editor-search-filters').find('gn-ui-button').eq(1).click()
cy.get('md-editor-search-filters')
.find('gn-ui-date-range-dropdown')
- .find('mat-icon')
- .should('contain.text', 'expand_less')
+ .find('ng-icon')
+ .should('have.attr', 'ng-reflect-name', 'matExpandLess')
})
})
describe('myRecords search filters', () => {
@@ -364,9 +375,145 @@ describe('dashboard (authenticated)', () => {
})
it('should filter the record list by last update (changeDate)', () => {
cy.get('md-editor-search-filters').find('gn-ui-button').first().click()
+ selectDateRange()
checkFilterByChangeDate()
})
})
+ describe('allRecord search filters summary', () => {
+ beforeEach(() => {
+ cy.visit('/catalog/search')
+ })
+ it('should not display anything without selected filters', () => {
+ cy.get('gn-ui-search-filters-summary-item').should('not.exist')
+ })
+ describe('selecting users', () => {
+ beforeEach(() => {
+ selectUser(1)
+ })
+ it('should display a label for badges of selected users', () => {
+ cy.get('gn-ui-search-filters-summary')
+ .find('[data-cy="filterSummaryLabel"]')
+ .invoke('text')
+ .should('eq', 'Modified by: ')
+ })
+ it('should display the badge for a selected user', () => {
+ cy.get('gn-ui-search-filters-summary')
+ .find('gn-ui-badge')
+ .should('have.length', 1)
+ cy.get('gn-ui-search-filters-summary')
+ .find('gn-ui-badge')
+ .invoke('text')
+ .should('eq', 'Barbara Roberts')
+ })
+ it('should display a second badge for a second selected user', () => {
+ selectUser(0, false)
+ cy.get('gn-ui-search-filters-summary')
+ .find('gn-ui-badge')
+ .should('have.length', 2)
+ cy.get('gn-ui-search-filters-summary')
+ .find('gn-ui-badge')
+ .eq(1)
+ .invoke('text')
+ .should('eq', 'admin admin')
+ })
+ it('should remove one of two badges when a badge cross is clicked', () => {
+ selectUser(0, false)
+ closeDropDown()
+ cy.get('gn-ui-search-filters-summary')
+ .find('gn-ui-badge')
+ .eq(0)
+ .find('ng-icon')
+ .click()
+ cy.get('gn-ui-search-filters-summary')
+ .find('gn-ui-badge')
+ .should('have.length', 1)
+ })
+ })
+ describe('selecting date range', () => {
+ beforeEach(() => {
+ cy.get('md-editor-search-filters').find('gn-ui-button').eq(1).click()
+ selectDateRange()
+ })
+ it('should display a label for the date range', () => {
+ cy.get('gn-ui-search-filters-summary')
+ .find('[data-cy="filterSummaryLabel"]')
+ .invoke('text')
+ .should('eq', 'Modified on: ')
+ })
+ it('should display the badge for the selected date range', () => {
+ cy.get('gn-ui-search-filters-summary')
+ .find('gn-ui-badge')
+ .invoke('text')
+ .should('eq', '01.08.2024 - 30.08.2024')
+ })
+ it('should remove the badge when the badge cross is clicked', () => {
+ closeDropDown()
+ cy.get('gn-ui-search-filters-summary')
+ .find('gn-ui-badge')
+ .find('ng-icon')
+ .click()
+ cy.get('gn-ui-search-filters-summary')
+ .find('gn-ui-badge')
+ .should('not.exist')
+ })
+ })
+ describe('selecting multiple filters (users and date range)', () => {
+ beforeEach(() => {
+ selectUser(0)
+ closeDropDown()
+ cy.get('md-editor-search-filters').find('gn-ui-button').eq(1).click()
+ selectDateRange()
+ })
+ it('should display both badges', () => {
+ cy.get('gn-ui-search-filters-summary')
+ .find('gn-ui-badge')
+ .should('have.length', 2)
+ })
+ it('should clear all filters when the clear button is clicked', () => {
+ cy.get('gn-ui-search-filters-summary').find('button').last().click()
+ cy.get('gn-ui-search-filters-summary')
+ .find('gn-ui-badge')
+ .should('have.length', 0)
+ cy.get('gn-ui-search-filters-summary-item').should('not.exist')
+ })
+ })
+ })
+ describe('myRecords search filters summary', () => {
+ beforeEach(() => {
+ cy.visit('/my-space/my-records')
+ })
+ it('should not display anything without selected filters', () => {
+ cy.get('gn-ui-search-filters-summary-item').should('not.exist')
+ })
+ describe('selecting date range', () => {
+ beforeEach(() => {
+ cy.get('md-editor-search-filters').find('gn-ui-button').eq(0).click()
+ selectDateRange()
+ })
+ it('should display a label for the date range', () => {
+ cy.get('gn-ui-search-filters-summary')
+ .find('[data-cy="filterSummaryLabel"]')
+ .invoke('text')
+ .should('eq', 'Modified on: ')
+ })
+ it('should display the badge for the selected date range', () => {
+ cy.get('gn-ui-search-filters-summary')
+ .find('gn-ui-badge')
+ .invoke('text')
+ .should('eq', '01.08.2024 - 30.08.2024')
+ })
+ it('should remove the badge when the badge cross is clicked', () => {
+ closeDropDown()
+ cy.get('gn-ui-search-filters-summary')
+ .find('gn-ui-badge')
+ .find('ng-icon')
+ .click()
+ cy.get('gn-ui-search-filters-summary')
+ .find('gn-ui-badge')
+ .should('not.exist')
+ })
+ })
+ })
})
describe('Account settings access', () => {
it('should navigate to the account settings page', () => {
diff --git a/apps/metadata-editor/src/app/dashboard/search-filters/search-filters.component.html b/apps/metadata-editor/src/app/dashboard/search-filters/search-filters.component.html
index 75710a0f5..3d4728505 100644
--- a/apps/metadata-editor/src/app/dashboard/search-filters/search-filters.component.html
+++ b/apps/metadata-editor/src/app/dashboard/search-filters/search-filters.component.html
@@ -1,13 +1,20 @@
diff --git a/apps/metadata-editor/src/app/dashboard/search-filters/search-filters.component.spec.ts b/apps/metadata-editor/src/app/dashboard/search-filters/search-filters.component.spec.ts
index 487bdb861..2b2d9a2fb 100644
--- a/apps/metadata-editor/src/app/dashboard/search-filters/search-filters.component.spec.ts
+++ b/apps/metadata-editor/src/app/dashboard/search-filters/search-filters.component.spec.ts
@@ -2,13 +2,17 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'
import { SearchFiltersComponent } from './search-filters.component'
import { MockBuilder } from 'ng-mocks'
import { TranslateModule } from '@ngx-translate/core'
+import { By } from '@angular/platform-browser'
+import { SearchFiltersSummaryComponent } from '@geonetwork-ui/feature/search'
describe('SearchFiltersComponent', () => {
let component: SearchFiltersComponent
let fixture: ComponentFixture
beforeEach(() => {
- return MockBuilder(SearchFiltersComponent)
+ return MockBuilder(SearchFiltersComponent).mock(
+ SearchFiltersSummaryComponent
+ )
})
beforeEach(async () => {
@@ -40,5 +44,15 @@ describe('SearchFiltersComponent', () => {
fixture.detectChanges()
expect(component.searchConfig).toEqual([])
})
+
+ it('should pass searchFields to SearchFiltersSummaryComponent', () => {
+ const searchFields = ['user', 'publisherOrg', 'format', 'isSpatial']
+ component.searchFields = searchFields
+ fixture.detectChanges()
+ const summaryComponent = fixture.debugElement.query(
+ By.directive(SearchFiltersSummaryComponent)
+ ).componentInstance
+ expect(summaryComponent.searchFields).toEqual(searchFields)
+ })
})
})
diff --git a/apps/metadata-editor/src/app/dashboard/search-filters/search-filters.component.ts b/apps/metadata-editor/src/app/dashboard/search-filters/search-filters.component.ts
index 68cc32e55..3b64b8a64 100644
--- a/apps/metadata-editor/src/app/dashboard/search-filters/search-filters.component.ts
+++ b/apps/metadata-editor/src/app/dashboard/search-filters/search-filters.component.ts
@@ -1,13 +1,16 @@
import { Component, Input, OnInit } from '@angular/core'
import { CommonModule } from '@angular/common'
import { TranslateModule } from '@ngx-translate/core'
-import { FeatureSearchModule } from '@geonetwork-ui/feature/search'
import {
NgIconComponent,
provideIcons,
provideNgIconsConfig,
} from '@ng-icons/core'
import { iconoirFilterList } from '@ng-icons/iconoir'
+import {
+ FeatureSearchModule,
+ SearchFiltersSummaryComponent,
+} from '@geonetwork-ui/feature/search'
@Component({
selector: 'md-editor-search-filters',
@@ -17,6 +20,7 @@ import { iconoirFilterList } from '@ng-icons/iconoir'
TranslateModule,
FeatureSearchModule,
NgIconComponent,
+ SearchFiltersSummaryComponent,
],
providers: [
provideIcons({
diff --git a/apps/metadata-editor/src/app/records/my-records/my-records.component.ts b/apps/metadata-editor/src/app/records/my-records/my-records.component.ts
index 2068fd36f..de10855ff 100644
--- a/apps/metadata-editor/src/app/records/my-records/my-records.component.ts
+++ b/apps/metadata-editor/src/app/records/my-records/my-records.component.ts
@@ -13,7 +13,7 @@ import { RecordsListComponent } from '../records-list.component'
import {
FeatureSearchModule,
FieldsService,
- ResultsTableContainerComponent,
+ FILTER_SUMMARY_IGNORE_LIST,
SearchFacade,
} from '@geonetwork-ui/feature/search'
import { PlatformServiceInterface } from '@geonetwork-ui/common/domain/platform.service.interface'
@@ -38,6 +38,8 @@ import {
iconoirPagePlus,
} from '@ng-icons/iconoir'
+const FILTER_OWNER = 'owner'
+
@Component({
selector: 'md-editor-my-records',
templateUrl: './my-records.component.html',
@@ -47,7 +49,6 @@ import {
CommonModule,
TranslateModule,
RecordsListComponent,
- ResultsTableContainerComponent,
UiElementsModule,
RecordsCountComponent,
ButtonComponent,
@@ -66,6 +67,7 @@ import {
provideNgIconsConfig({
size: '1.5rem',
}),
+ { provide: FILTER_SUMMARY_IGNORE_LIST, useValue: [FILTER_OWNER] },
],
})
export class MyRecordsComponent implements OnInit {
@@ -93,7 +95,7 @@ export class MyRecordsComponent implements OnInit {
this.platformService.getMe().subscribe((user) => {
this.fieldsService
- .buildFiltersFromFieldValues({ owner: user.id })
+ .buildFiltersFromFieldValues({ [FILTER_OWNER]: user.id })
.subscribe((filters) => {
this.searchFacade.updateFilters(filters)
})
diff --git a/libs/feature/router/src/lib/default/state/query-params.utils.spec.ts b/libs/feature/router/src/lib/default/state/query-params.utils.spec.ts
index 08393b9ec..ec3c3ab1b 100644
--- a/libs/feature/router/src/lib/default/state/query-params.utils.spec.ts
+++ b/libs/feature/router/src/lib/default/state/query-params.utils.spec.ts
@@ -37,11 +37,11 @@ describe('query params utilities', () => {
[ROUTE_PARAMS.SORT]: 'createDate',
publisher: ['john', 'barbie'],
updateDate: {
- start: new Date('2010-03-10'),
- end: new Date('2014-01-01'),
+ start: new Date('2010-03-10T00:00:00'),
+ end: new Date('2014-01-01T00:00:00'),
},
changeDate: {
- end: new Date('2008-08-14'),
+ end: new Date('2008-08-14T00:00:00'),
},
})
})
@@ -51,7 +51,7 @@ describe('query params utilities', () => {
})
expect(params).toEqual({
changeDate: {
- end: new Date('2008-08-14'),
+ end: new Date('2008-08-14T00:00:00'),
},
})
})
diff --git a/libs/feature/router/src/lib/default/state/query-params.utils.ts b/libs/feature/router/src/lib/default/state/query-params.utils.ts
index 73ea22fa8..133d5dbc7 100644
--- a/libs/feature/router/src/lib/default/state/query-params.utils.ts
+++ b/libs/feature/router/src/lib/default/state/query-params.utils.ts
@@ -1,4 +1,8 @@
-import { DateRange, isDateRange } from '@geonetwork-ui/api/repository'
+import {
+ DateRange,
+ formatDate,
+ isDateRange,
+} from '@geonetwork-ui/api/repository'
import { ROUTE_PARAMS, SearchRouteParams } from '../constants'
export function flattenQueryParams(
@@ -12,12 +16,10 @@ export function flattenQueryParams(
) {
flattened[key] = [(flattened[key] as string[]).join(',')]
} else if (isDateRange(flattened[key] as DateRange)) {
+ const start = (flattened[key] as DateRange).start
+ const end = (flattened[key] as DateRange).end
flattened[key] = [
- `${
- (flattened[key] as DateRange).start?.toISOString().split('T')[0] || ''
- }..${
- (flattened[key] as DateRange).end?.toISOString().split('T')[0] || ''
- }`,
+ `${start ? formatDate(start) : ''}..${formatDate(end) || ''}`,
]
}
}
@@ -42,8 +44,8 @@ export function expandQueryParams(
} else if (isDateUrl(value)) {
const [start, end] = value.split('..')
expanded[key] = {
- ...(start && { start: new Date(start) }),
- ...(end && { end: new Date(end) }),
+ ...(start && { start: new Date(`${start}T00:00:00`) }),
+ ...(end && { end: new Date(`${end}T00:00:00`) }),
}
} else {
expanded[key] = value.split(',')
diff --git a/libs/feature/search/src/index.ts b/libs/feature/search/src/index.ts
index f6103754e..63ae62fd7 100644
--- a/libs/feature/search/src/index.ts
+++ b/libs/feature/search/src/index.ts
@@ -22,3 +22,5 @@ export * from './lib/results-layout/results-layout.component'
export * from './lib/sort-by/sort-by.component'
export * from './lib/state/container/search-state.container.directive'
export * from './lib/results-table/results-table-container.component'
+export * from './lib/search-filters-summary/search-filters-summary.component'
+export * from './lib/search-filters-summary-item/search-filters-summary-item.component'
diff --git a/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.ts b/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.ts
index d5bb87d07..33f7d30ad 100644
--- a/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.ts
+++ b/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.ts
@@ -72,13 +72,11 @@ export class FilterDropdownComponent implements OnInit {
onStartDateChange(start: Date) {
if (!start) return
- start.setUTCHours(24, 0, 0, 0)
this.dateRange = { ...this.dateRange, start }
}
onEndDateChange(end: Date) {
if (!end) return
- end.setUTCHours(24, 0, 0, 0)
this.dateRange = { ...this.dateRange, end }
if (this.dateRange.start && this.dateRange.end) {
this.fieldsService
diff --git a/libs/feature/search/src/lib/search-filters-summary-item/search-filters-summary-item.component.css b/libs/feature/search/src/lib/search-filters-summary-item/search-filters-summary-item.component.css
new file mode 100644
index 000000000..e69de29bb
diff --git a/libs/feature/search/src/lib/search-filters-summary-item/search-filters-summary-item.component.html b/libs/feature/search/src/lib/search-filters-summary-item/search-filters-summary-item.component.html
new file mode 100644
index 000000000..558703608
--- /dev/null
+++ b/libs/feature/search/src/lib/search-filters-summary-item/search-filters-summary-item.component.html
@@ -0,0 +1,20 @@
+ 0"
+ class="flex flex-row items-center gap-2"
+>
+ {{
+ translatedLabel
+ }}
+ {{ fieldValue.label }}
+
diff --git a/libs/feature/search/src/lib/search-filters-summary-item/search-filters-summary-item.component.spec.ts b/libs/feature/search/src/lib/search-filters-summary-item/search-filters-summary-item.component.spec.ts
new file mode 100644
index 000000000..45bf0fdb3
--- /dev/null
+++ b/libs/feature/search/src/lib/search-filters-summary-item/search-filters-summary-item.component.spec.ts
@@ -0,0 +1,155 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing'
+
+import { SearchFiltersSummaryItemComponent } from './search-filters-summary-item.component'
+import { BehaviorSubject, firstValueFrom, of } from 'rxjs'
+import { MockBuilder, MockComponent, MockProvider } from 'ng-mocks'
+import { SearchFacade } from '../state/search.facade'
+import { SearchService } from '../utils/service/search.service'
+import { FieldFilters } from '@geonetwork-ui/common/domain/model/search'
+import { BadgeComponent } from '@geonetwork-ui/ui/inputs'
+import { CommonModule, DatePipe } from '@angular/common'
+import { TranslateModule, TranslateService } from '@ngx-translate/core'
+import { FieldsService } from '../utils/service/fields.service'
+import { FieldType } from '../utils/service/fields'
+
+const FIELD_VALUES_FROM_FILTERS_MOCK = {
+ organization: [],
+ format: [],
+ resourceType: [],
+ representationType: [],
+ publicationYear: [],
+ topic: [],
+ inspireKeyword: [],
+ keyword: [],
+ documentStandard: [],
+ isSpatial: [],
+ q: [],
+ license: [],
+ owner: [],
+ producerOrg: [],
+ publisherOrg: [],
+ user: ['admin|admin|admin|Administrator', 'barbie|Roberts|Barbara|UserAdmin'],
+ changeDate: {
+ start: new Date('2024-11-01T00:00:00.000Z'),
+ end: new Date('2024-11-29T00:00:00.000Z'),
+ },
+}
+/* searchFilters$ is only used to trigger change detection.
+ ** its value is replaced by FIELD_VALUES_FROM_FILTERS_MOCK in stream
+ */
+class SearchFacadeMock {
+ searchFilters$ = new BehaviorSubject({})
+}
+class SearchServiceMock {
+ setFilters = jest.fn()
+}
+
+class TranslateServiceMock {
+ get = jest.fn(() => of(''))
+}
+
+describe('SearchFiltersSummaryItemComponent', () => {
+ let component: SearchFiltersSummaryItemComponent
+ let fixture: ComponentFixture
+ let searchFacade: SearchFacade
+ let translateService: TranslateService
+ let fieldsService: FieldsService
+
+ beforeEach(() => {
+ return MockBuilder(SearchFiltersSummaryItemComponent)
+ })
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [CommonModule, TranslateModule.forRoot()],
+ declarations: [
+ SearchFiltersSummaryItemComponent,
+ MockComponent(BadgeComponent),
+ ],
+ providers: [
+ MockProvider(SearchFacade, SearchFacadeMock, 'useClass'),
+ MockProvider(SearchService, SearchServiceMock, 'useClass'),
+ MockProvider(FieldsService),
+ MockProvider(DatePipe),
+ MockProvider(TranslateService, TranslateServiceMock, 'useClass'),
+ ],
+ }).compileComponents()
+ fixture = TestBed.createComponent(SearchFiltersSummaryItemComponent)
+ component = fixture.componentInstance
+ searchFacade = TestBed.inject(SearchFacade)
+ fieldsService = TestBed.inject(FieldsService)
+ translateService = TestBed.inject(TranslateService)
+ })
+
+ it('should create', () => {
+ expect(component).toBeTruthy()
+ })
+
+ it('should set fieldValues$ observable for empty filters', async () => {
+ const fieldValues = await firstValueFrom(component.fieldValues$)
+ expect(fieldValues).toEqual([])
+ })
+
+ describe('fieldValues$', () => {
+ beforeEach(() => {
+ fieldsService.getFieldType = jest.fn(
+ (field: 'changeDate' | 'user') =>
+ (field === 'changeDate' ? 'dateRange' : 'values') as FieldType
+ )
+ fieldsService.readFieldValuesFromFilters = jest.fn(() =>
+ of(FIELD_VALUES_FROM_FILTERS_MOCK)
+ )
+ ;(searchFacade.searchFilters$ as BehaviorSubject).next({})
+ })
+ it('should set fieldValues$ observable for user values filters', async () => {
+ component.fieldName = 'user'
+ const fieldValues = await firstValueFrom(component.fieldValues$)
+ expect(fieldValues).toEqual([
+ {
+ value: 'admin|admin|admin|Administrator',
+ label: 'admin admin',
+ },
+ {
+ value: 'barbie|Roberts|Barbara|UserAdmin',
+ label: 'Barbara Roberts',
+ },
+ ])
+ })
+ it('should set fieldValues$ observable for changeDate dateRange filters', async () => {
+ component.fieldName = 'changeDate'
+ fixture.detectChanges()
+ const fieldValues = await firstValueFrom(component.fieldValues$)
+ expect(fieldValues).toEqual([
+ {
+ value: {
+ start: new Date('2024-11-01T00:00:00.000Z'),
+ end: new Date('2024-11-29T00:00:00.000Z'),
+ },
+ label: '01.11.2024 - 29.11.2024',
+ },
+ ])
+ })
+ })
+
+ describe('translateLabel', () => {
+ const fieldName = 'user'
+ const labelKey = `search.filters.summaryLabel.${fieldName}`
+ const fallbackKey = `search.filters.${fieldName}`
+ beforeEach(() => {
+ component.fieldName = fieldName
+ fixture.detectChanges()
+ translateService.get = jest.fn((key) => {
+ if (key === labelKey) {
+ return of(labelKey) // Simulate missing translation
+ } else if (key === fallbackKey) {
+ return of('Fallback Label')
+ }
+ return of('')
+ })
+ })
+ it('should translate label with fallback if necessary', () => {
+ component.translateLabel()
+ expect(component.translatedLabel).toBe('Fallback Label')
+ })
+ })
+})
diff --git a/libs/feature/search/src/lib/search-filters-summary-item/search-filters-summary-item.component.ts b/libs/feature/search/src/lib/search-filters-summary-item/search-filters-summary-item.component.ts
new file mode 100644
index 000000000..024a0217c
--- /dev/null
+++ b/libs/feature/search/src/lib/search-filters-summary-item/search-filters-summary-item.component.ts
@@ -0,0 +1,118 @@
+import { Component, Input, OnInit } from '@angular/core'
+import { CommonModule, DatePipe } from '@angular/common'
+import {
+ catchError,
+ firstValueFrom,
+ map,
+ Observable,
+ of,
+ switchMap,
+} from 'rxjs'
+import { BadgeComponent } from '@geonetwork-ui/ui/inputs'
+import { TranslateModule, TranslateService } from '@ngx-translate/core'
+import { DateRange } from '@geonetwork-ui/api/repository'
+import { FieldType, FieldValue } from '../utils/service/fields'
+import { SearchFacade } from '../state/search.facade'
+import { SearchService } from '../utils/service/search.service'
+import { FieldsService } from '../utils/service/fields.service'
+import { formatUserInfo } from '@geonetwork-ui/util/shared'
+import { marker } from '@biesbjerg/ngx-translate-extract-marker'
+
+marker('search.filters.summaryLabel.user')
+marker('search.filters.summaryLabel.changeDate')
+
+interface DisplayedValue {
+ label: string
+ value: FieldValue | DateRange
+}
+
+@Component({
+ selector: 'gn-ui-search-filters-summary-item',
+ standalone: true,
+ imports: [CommonModule, TranslateModule, BadgeComponent],
+ templateUrl: './search-filters-summary-item.component.html',
+ styleUrls: ['./search-filters-summary-item.component.css'],
+ providers: [DatePipe],
+})
+export class SearchFiltersSummaryItemComponent implements OnInit {
+ @Input() fieldName: string
+ fieldType: FieldType
+ translatedLabel: string
+
+ fieldValues$ = this.searchFacade.searchFilters$.pipe(
+ switchMap((filters) =>
+ this.fieldsService.readFieldValuesFromFilters(filters)
+ ),
+ map((fieldValues) =>
+ Array.isArray(fieldValues[this.fieldName])
+ ? (fieldValues[this.fieldName] as FieldValue[])
+ : ([fieldValues[this.fieldName]] as FieldValue[])
+ ),
+ map(
+ (fieldValues) => this.getReadableValues(fieldValues) as DisplayedValue[]
+ ),
+ catchError(() => of([]))
+ ) as Observable
+
+ constructor(
+ private searchFacade: SearchFacade,
+ private searchService: SearchService,
+ private fieldsService: FieldsService,
+ private datePipe: DatePipe,
+ private translate: TranslateService
+ ) {}
+
+ ngOnInit() {
+ this.fieldType = this.fieldsService.getFieldType(this.fieldName)
+ this.translateLabel()
+ }
+
+ translateLabel() {
+ const labelKey = `search.filters.summaryLabel.${this.fieldName}`
+ const fallbackKey = `search.filters.${this.fieldName}`
+ this.translate.get(labelKey).subscribe((value: string) => {
+ if (value === labelKey) {
+ this.translate.get(fallbackKey).subscribe((fallbackValue: string) => {
+ this.translatedLabel = fallbackValue
+ })
+ } else {
+ this.translatedLabel = value
+ }
+ })
+ }
+
+ getReadableValues(fieldValues: FieldValue[] | DateRange[]): DisplayedValue[] {
+ return fieldValues.map((value) => {
+ if (this.fieldType === 'dateRange') {
+ return {
+ value,
+ label: `${this.datePipe.transform(
+ value.start,
+ 'dd.MM.yyyy'
+ )} - ${this.datePipe.transform(value.end, 'dd.MM.yyyy')}`,
+ }
+ } else if (this.fieldName === 'user') {
+ return { value, label: formatUserInfo(value) }
+ } else {
+ return { value, label: value }
+ }
+ })
+ }
+
+ async removeFilterValue(fieldValue: FieldValue | DateRange) {
+ const currentFieldValues: DisplayedValue[] = await firstValueFrom(
+ this.fieldValues$
+ )
+ const updatedFieldValues = currentFieldValues
+ .filter(
+ (displayedValue: DisplayedValue) => displayedValue.value !== fieldValue
+ )
+ .map((displayedValue: DisplayedValue) => displayedValue.value)
+
+ this.fieldsService
+ .buildFiltersFromFieldValues({
+ [this.fieldName]: updatedFieldValues as FieldValue[],
+ })
+ .subscribe((filters) => this.searchService.updateFilters(filters))
+ }
+}
diff --git a/libs/feature/search/src/lib/search-filters-summary/search-filters-summary.component.css b/libs/feature/search/src/lib/search-filters-summary/search-filters-summary.component.css
new file mode 100644
index 000000000..e69de29bb
diff --git a/libs/feature/search/src/lib/search-filters-summary/search-filters-summary.component.html b/libs/feature/search/src/lib/search-filters-summary/search-filters-summary.component.html
new file mode 100644
index 000000000..289a9d994
--- /dev/null
+++ b/libs/feature/search/src/lib/search-filters-summary/search-filters-summary.component.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
diff --git a/libs/feature/search/src/lib/search-filters-summary/search-filters-summary.component.spec.ts b/libs/feature/search/src/lib/search-filters-summary/search-filters-summary.component.spec.ts
new file mode 100644
index 000000000..8056ac459
--- /dev/null
+++ b/libs/feature/search/src/lib/search-filters-summary/search-filters-summary.component.spec.ts
@@ -0,0 +1,156 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing'
+import {
+ FILTER_SUMMARY_IGNORE_LIST,
+ SearchFiltersSummaryComponent,
+} from './search-filters-summary.component'
+import { MockComponent, MockProvider } from 'ng-mocks'
+import { SearchService } from '../utils/service/search.service'
+import { SearchFacade } from '../state/search.facade'
+import { BehaviorSubject, firstValueFrom } from 'rxjs'
+import { TranslateModule } from '@ngx-translate/core'
+import { SearchFiltersSummaryItemComponent } from '../search-filters-summary-item/search-filters-summary-item.component'
+import { FieldFilters } from '@geonetwork-ui/common/domain/model/search'
+
+class SearchFacadeMock {
+ searchFilters$ = new BehaviorSubject({
+ format: {},
+ isSpatial: {},
+ license: {},
+ 'userInfo.keyword': {},
+ })
+}
+class SearchServiceMock {
+ setFilters = jest.fn()
+}
+
+describe('SearchFiltersSummaryComponent', () => {
+ let component: SearchFiltersSummaryComponent
+ let fixture: ComponentFixture
+ let searchFacade: SearchFacade
+ let searchService: SearchService
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [TranslateModule.forRoot()],
+ declarations: [
+ SearchFiltersSummaryComponent,
+ MockComponent(SearchFiltersSummaryItemComponent),
+ ],
+ providers: [
+ MockProvider(SearchFacade, SearchFacadeMock, 'useClass'),
+ MockProvider(SearchService, SearchServiceMock, 'useClass'),
+ ],
+ })
+ })
+
+ describe('no injection token provided', () => {
+ beforeEach(async () => {
+ await TestBed.compileComponents()
+ fixture = TestBed.createComponent(SearchFiltersSummaryComponent)
+ searchFacade = TestBed.inject(SearchFacade)
+ searchService = TestBed.inject(SearchService)
+ component = fixture.componentInstance
+ fixture.detectChanges()
+ })
+ it('should create', () => {
+ expect(component).toBeTruthy()
+ })
+
+ it('should set searchFilterActive$ observable to false for empty filters', async () => {
+ const isActive = await firstValueFrom(component.searchFilterActive$)
+ expect(isActive).toBeFalsy()
+ })
+
+ it('should set searchFilterActive$ observable to true for NON empty value filters', async () => {
+ const filters = {
+ format: {},
+ isSpatial: {},
+ license: {},
+ 'userInfo.keyword': { 'admin|admin|admin|Administrator': true },
+ }
+ ;(searchFacade.searchFilters$ as BehaviorSubject).next(
+ filters
+ )
+ const isActive = await firstValueFrom(component.searchFilterActive$)
+ expect(isActive).toBeTruthy()
+ })
+
+ it('should set searchFilterActive$ observable to true for NON empty dateRange filters', async () => {
+ const filters = {
+ format: {},
+ isSpatial: {},
+ license: {},
+ changeDate: {
+ start: new Date('2021-01-01'),
+ end: new Date('2021-01-02'),
+ },
+ }
+ ;(searchFacade.searchFilters$ as BehaviorSubject).next(
+ filters
+ )
+ const isActive = await firstValueFrom(component.searchFilterActive$)
+ expect(isActive).toBeTruthy()
+ })
+
+ it('should clear filters', () => {
+ component.clearFilters()
+ expect(searchService.setFilters).toHaveBeenCalledWith({})
+ })
+ })
+
+ describe('FILTER_SUMMARY_IGNORE_LIST injection token provided', () => {
+ beforeEach(async () => {
+ TestBed.overrideProvider(FILTER_SUMMARY_IGNORE_LIST, {
+ useValue: ['owner'],
+ })
+ await TestBed.compileComponents()
+ fixture = TestBed.createComponent(SearchFiltersSummaryComponent)
+ searchFacade = TestBed.inject(SearchFacade)
+ searchService = TestBed.inject(SearchService)
+ component = fixture.componentInstance
+ fixture.detectChanges()
+ })
+ it('should ignore filters from FILTER_SUMMARY_IGNORE_LIST', async () => {
+ const filters = {
+ owner: { 1: true },
+ isSpatial: {},
+ license: {},
+ }
+ ;(searchFacade.searchFilters$ as BehaviorSubject).next(
+ filters
+ )
+ const isActive = await firstValueFrom(component.searchFilterActive$)
+ expect(isActive).toBeFalsy()
+ })
+ it('should set searchFilterActive$ observable to true for NON empty value filters', async () => {
+ const filters = {
+ owner: { 1: true },
+ format: {},
+ isSpatial: {},
+ license: {},
+ 'userInfo.keyword': { 'admin|admin|admin|Administrator': true },
+ }
+ ;(searchFacade.searchFilters$ as BehaviorSubject).next(
+ filters
+ )
+ const isActive = await firstValueFrom(component.searchFilterActive$)
+ expect(isActive).toBeTruthy()
+ })
+ it('should clear filters except with keys from FILTER_SUMMARY_IGNORE_LIST', () => {
+ const filters = {
+ owner: { 1: true },
+ format: {},
+ isSpatial: {},
+ license: {},
+ 'userInfo.keyword': { 'admin|admin|admin|Administrator': true },
+ }
+ ;(searchFacade.searchFilters$ as BehaviorSubject).next(
+ filters
+ )
+ component.clearFilters()
+ expect(searchService.setFilters).toHaveBeenCalledWith({
+ owner: { 1: true },
+ })
+ })
+ })
+})
diff --git a/libs/feature/search/src/lib/search-filters-summary/search-filters-summary.component.ts b/libs/feature/search/src/lib/search-filters-summary/search-filters-summary.component.ts
new file mode 100644
index 000000000..c4318f8f7
--- /dev/null
+++ b/libs/feature/search/src/lib/search-filters-summary/search-filters-summary.component.ts
@@ -0,0 +1,74 @@
+import { Component, Inject, Input, OnInit, Optional } from '@angular/core'
+import { CommonModule } from '@angular/common'
+import { first, map, Observable } from 'rxjs'
+import { SearchFiltersSummaryItemComponent } from '../search-filters-summary-item/search-filters-summary-item.component'
+import { TranslateModule } from '@ngx-translate/core'
+import { SearchFacade } from '../state/search.facade'
+import { SearchService } from '../utils/service/search.service'
+import { FieldFilters } from '@geonetwork-ui/common/domain/model/search'
+import { InjectionToken } from '@angular/core'
+
+export const FILTER_SUMMARY_IGNORE_LIST = new InjectionToken(
+ 'FILTER_SUMMARY_IGNORE_LIST'
+)
+@Component({
+ selector: 'gn-ui-search-filters-summary',
+ imports: [CommonModule, SearchFiltersSummaryItemComponent, TranslateModule],
+ templateUrl: './search-filters-summary.component.html',
+ styleUrls: ['./search-filters-summary.component.css'],
+ standalone: true,
+})
+export class SearchFiltersSummaryComponent implements OnInit {
+ @Input() searchFields: string[] = []
+ filterSummaryIgnoreList: string[]
+
+ searchFilterActive$: Observable
+
+ constructor(
+ private searchFacade: SearchFacade,
+ private searchService: SearchService,
+ @Optional()
+ @Inject(FILTER_SUMMARY_IGNORE_LIST)
+ filterSummaryIgnoreList: string[]
+ ) {
+ this.filterSummaryIgnoreList = filterSummaryIgnoreList || []
+ }
+
+ ngOnInit(): void {
+ this.searchFilterActive$ = this.searchFacade.searchFilters$.pipe(
+ map((filters) => this.hasNonEmptyValues(filters))
+ )
+ }
+
+ hasNonEmptyValues(filters: FieldFilters): boolean {
+ const filteredFilters = {}
+ for (const [key, value] of Object.entries(filters)) {
+ if (!this.filterSummaryIgnoreList.includes(key)) {
+ filteredFilters[key] = value
+ }
+ }
+ return Object.values(filteredFilters).some(
+ (value) =>
+ value !== undefined &&
+ (typeof value !== 'object' ||
+ (typeof value === 'object' && Object.keys(value).length > 0))
+ )
+ }
+
+ clearFilters() {
+ this.searchFacade.searchFilters$
+ .pipe(
+ first(),
+ map((filters) => {
+ const newFilters = { ...filters }
+ Object.keys(newFilters).forEach((key) => {
+ if (!this.filterSummaryIgnoreList.includes(key)) {
+ delete newFilters[key]
+ }
+ })
+ return newFilters
+ })
+ )
+ .subscribe((filters) => this.searchService.setFilters(filters))
+ }
+}
diff --git a/libs/feature/search/src/lib/utils/service/fields.spec.ts b/libs/feature/search/src/lib/utils/service/fields.spec.ts
index 95b2fce3e..4d8ba4138 100644
--- a/libs/feature/search/src/lib/utils/service/fields.spec.ts
+++ b/libs/feature/search/src/lib/utils/service/fields.spec.ts
@@ -275,10 +275,10 @@ describe('search fields implementations', () => {
let filter
beforeEach(async () => {
filter = await lastValueFrom(
- searchField.getFiltersForValues(['First value', 'Second value'])
+ searchField.getFiltersForValues(['First value', 'Second value', ''])
)
})
- it('returns appropriate filters', () => {
+ it('returns appropriate filters (ignoring empty strings)', () => {
expect(filter).toEqual({
myField: {
'First value': true,
@@ -287,6 +287,17 @@ describe('search fields implementations', () => {
})
})
})
+ describe('#getFiltersForValues with empty value', () => {
+ let filter
+ beforeEach(async () => {
+ filter = await lastValueFrom(searchField.getFiltersForValues(['']))
+ })
+ it('returns empty filter', () => {
+ expect(filter).toEqual({
+ myField: {},
+ })
+ })
+ })
describe('#getValuesForFilters', () => {
let values
describe('with several values', () => {
@@ -362,6 +373,17 @@ describe('search fields implementations', () => {
})
})
})
+ describe('#getFiltersForValues with empty value', () => {
+ let filter
+ beforeEach(async () => {
+ filter = await lastValueFrom(searchField.getFiltersForValues(['']))
+ })
+ it('returns empty filter', () => {
+ expect(filter).toEqual({
+ changeDate: {},
+ })
+ })
+ })
describe('#getValuesForFilters', () => {
let values
describe('with several values', () => {
diff --git a/libs/feature/search/src/lib/utils/service/fields.ts b/libs/feature/search/src/lib/utils/service/fields.ts
index 3e5620043..1d8422ba0 100644
--- a/libs/feature/search/src/lib/utils/service/fields.ts
+++ b/libs/feature/search/src/lib/utils/service/fields.ts
@@ -20,6 +20,7 @@ import {
METADATA_LANGUAGE,
} from '@geonetwork-ui/api/repository'
import { LangService } from '@geonetwork-ui/util/i18n'
+import { formatUserInfo } from '@geonetwork-ui/util/shared'
export type FieldType = 'values' | 'dateRange'
@@ -83,18 +84,24 @@ export class SimpleSearchField implements AbstractSearchField {
})
)
}
- getFiltersForValues(values: FieldValue[] | DateRange[]): Observable {
+ getFiltersForValues(
+ values: FieldValue[] | DateRange[]
+ ): Observable {
// FieldValue[]
if (this.getType() === 'values') {
return of({
[this.esFieldName]: (values as FieldValue[]).reduce((acc, val) => {
- return { ...acc, [val.toString()]: true }
+ const value = val.toString()
+ if (value !== '') {
+ return { ...acc, [value]: true }
+ }
+ return acc
}, {}),
})
}
// DateRange
return of({
- [this.esFieldName]: values[0],
+ [this.esFieldName]: values[0] !== '' ? values[0] : {},
})
}
getValuesForFilter(
@@ -392,20 +399,11 @@ export class UserSearchField extends SimpleSearchField {
map((values) =>
values.map((v) => ({
...v,
- label: this.formatUserInfo(v.label),
+ label: formatUserInfo(v.label, true),
}))
)
)
}
-
- private formatUserInfo(userInfo: string | unknown): string {
- const infos = (typeof userInfo === 'string' ? userInfo : '').split('|')
- const count = infos[3].split(' ')[1]
- if (infos && infos.length === 4) {
- return `${infos[2]} ${infos[1]} ${count}`
- }
- return undefined
- }
}
export class DateRangeSearchField extends SimpleSearchField {
diff --git a/libs/ui/inputs/src/lib/badge/badge.component.html b/libs/ui/inputs/src/lib/badge/badge.component.html
index b2e838d45..1eb95970e 100644
--- a/libs/ui/inputs/src/lib/badge/badge.component.html
+++ b/libs/ui/inputs/src/lib/badge/badge.component.html
@@ -18,7 +18,7 @@
--gn-ui-button-width: 1.4em;
--gn-ui-button-height: 1.4em;
--gn-ui-button-rounded: 1.4em;
- --gn-ui-button-background: white;
+ --gn-ui-button-background: var(--gn-ui-badge-background-color, white);
"
>
diff --git a/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.html b/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.html
index 1ae135436..e8d138ea6 100644
--- a/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.html
+++ b/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.html
@@ -8,10 +8,11 @@
{{ title }}
-
- expand_less
- expand_more
-
+
+
diff --git a/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.ts b/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.ts
index 14f6c88ae..82447a1ba 100644
--- a/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.ts
+++ b/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.ts
@@ -9,24 +9,29 @@ import {
ViewChild,
} from '@angular/core'
import { CommonModule } from '@angular/common'
-import { DateRangePickerComponent } from '../date-range-picker/date-range-picker.component'
-import { MatIconModule } from '@angular/material/icon'
import { MatNativeDateModule } from '@angular/material/core'
import { MatDatepickerModule } from '@angular/material/datepicker'
import { ButtonComponent } from '../button/button.component'
import { OverlayContainer } from '@angular/cdk/overlay'
+import { NgIconComponent, provideIcons } from '@ng-icons/core'
+import { matExpandLess, matExpandMore } from '@ng-icons/material-icons/baseline'
@Component({
selector: 'gn-ui-date-range-dropdown',
standalone: true,
imports: [
CommonModule,
- DateRangePickerComponent,
- MatIconModule,
+ NgIconComponent,
MatNativeDateModule,
MatDatepickerModule,
ButtonComponent,
],
+ providers: [
+ provideIcons({
+ matExpandMore,
+ matExpandLess,
+ }),
+ ],
templateUrl: './date-range-dropdown.component.html',
styleUrls: ['./date-range-dropdown.component.css'],
})
diff --git a/libs/ui/search/src/lib/results-table/results-table.component.ts b/libs/ui/search/src/lib/results-table/results-table.component.ts
index 78a5aa689..fc2ed7755 100644
--- a/libs/ui/search/src/lib/results-table/results-table.component.ts
+++ b/libs/ui/search/src/lib/results-table/results-table.component.ts
@@ -22,6 +22,7 @@ import {
} from '@geonetwork-ui/ui/layout'
import {
FileFormat,
+ formatUserInfo,
getBadgeColor,
getFileFormat,
getFormatPriority,
@@ -162,11 +163,7 @@ export class ResultsTableComponent {
}
formatUserInfo(userInfo: string | unknown): string {
- const infos = (typeof userInfo === 'string' ? userInfo : '').split('|')
- if (infos && infos.length === 4) {
- return `${infos[2]} ${infos[1]}`
- }
- return undefined
+ return formatUserInfo(userInfo)
}
getBadgeColor(format: FileFormat): string {
diff --git a/libs/util/shared/src/lib/utils/format-fields.spec.ts b/libs/util/shared/src/lib/utils/format-fields.spec.ts
new file mode 100644
index 000000000..6420ccd4a
--- /dev/null
+++ b/libs/util/shared/src/lib/utils/format-fields.spec.ts
@@ -0,0 +1,23 @@
+import { formatUserInfo } from './format-fields'
+
+describe('formatUserInfo', () => {
+ it('should format user info correctly', () => {
+ expect(formatUserInfo('barbie|Roberts|Barbara|UserAdmin (5)')).toEqual(
+ 'Barbara Roberts'
+ )
+ })
+
+ it('should format user info correctly with count', () => {
+ expect(
+ formatUserInfo('barbie|Roberts|Barbara|UserAdmin (5)', true)
+ ).toEqual('Barbara Roberts (5)')
+ })
+
+ it('should return undefined if user info is empty', () => {
+ expect(formatUserInfo('')).toBeUndefined()
+ })
+
+ it('should return undefined if user info is not a string', () => {
+ expect(formatUserInfo(undefined)).toBeUndefined()
+ })
+})
diff --git a/libs/util/shared/src/lib/utils/format-fields.ts b/libs/util/shared/src/lib/utils/format-fields.ts
new file mode 100644
index 000000000..f3604251d
--- /dev/null
+++ b/libs/util/shared/src/lib/utils/format-fields.ts
@@ -0,0 +1,11 @@
+export function formatUserInfo(
+ userInfo: string | unknown,
+ displayCount = false
+): string {
+ const infos = (typeof userInfo === 'string' ? userInfo : '').split('|')
+ const count = displayCount ? ` ${infos[3].split(' ')[1]}` : ''
+ if (infos && infos.length === 4) {
+ return `${infos[2]} ${infos[1]}${count}`
+ }
+ return undefined
+}
diff --git a/libs/util/shared/src/lib/utils/index.ts b/libs/util/shared/src/lib/utils/index.ts
index 301dce35e..6ca3d5c95 100644
--- a/libs/util/shared/src/lib/utils/index.ts
+++ b/libs/util/shared/src/lib/utils/index.ts
@@ -1,5 +1,6 @@
export * from './bytes-convert'
export * from './event'
+export * from './format-fields'
export * from './fuzzy-filter'
export * from './geojson'
export * from './image-resize'
diff --git a/tailwind.base.css b/tailwind.base.css
index ad0ec12ad..763815001 100644
--- a/tailwind.base.css
+++ b/tailwind.base.css
@@ -143,12 +143,13 @@
.gn-ui-badge {
--rounded: var(--gn-ui-badge-rounded, 0.25em);
--padding: var(--gn-ui-badge-padding, 0.375em 0.75em);
+ --font-weight: var(--gn-ui-badge-font-weight, 500);
--text-size: var(--gn-ui-badge-text-size, 0.875em);
--text-color: var(--gn-ui-badge-text-color, var(--color-gray-50));
--background-color: var(--gn-ui-badge-background-color, black);
--opacity: var(--gn-ui-badge-opacity, 0.7);
@apply opacity-[--opacity] p-[--padding] rounded-[--rounded]
- font-medium text-[length:--text-size] text-[color:--text-color] bg-[color:--background-color] flex justify-center items-center content-center;
+ font-[--font-weight] text-[length:--text-size] text-[color:--text-color] bg-[color:--background-color] flex justify-center items-center content-center;
}
/* makes sure icons will not make the badges grow vertically; also make size proportional */
.gn-ui-badge ng-icon {
diff --git a/translations/de.json b/translations/de.json
index d1d2bc7b0..dae4a058c 100644
--- a/translations/de.json
+++ b/translations/de.json
@@ -527,6 +527,8 @@
"search.filters.representationType": "Repräsentationstyp",
"search.filters.resourceType": "Ressourcentyp",
"search.filters.standard": "Standard",
+ "search.filters.summaryLabel.changeDate": "Geändert am: ",
+ "search.filters.summaryLabel.user": "Geändert von: ",
"search.filters.title": "Ergebnisse filtern",
"search.filters.topic": "Themen",
"search.filters.useSpatialFilter": "Zuerst Datensätze im Interessenbereich anzeigen",
diff --git a/translations/en.json b/translations/en.json
index a29733661..4b8fda7ba 100644
--- a/translations/en.json
+++ b/translations/en.json
@@ -527,6 +527,8 @@
"search.filters.representationType": "Representation type",
"search.filters.resourceType": "Resource type",
"search.filters.standard": "Standard",
+ "search.filters.summaryLabel.changeDate": "Modified on: ",
+ "search.filters.summaryLabel.user": "Modified by: ",
"search.filters.title": "Filter your results",
"search.filters.topic": "Topics",
"search.filters.useSpatialFilter": "Show records in the area of interest first",
diff --git a/translations/es.json b/translations/es.json
index 185b8bf02..2d179493d 100644
--- a/translations/es.json
+++ b/translations/es.json
@@ -527,6 +527,8 @@
"search.filters.representationType": "",
"search.filters.resourceType": "",
"search.filters.standard": "",
+ "search.filters.summaryLabel.changeDate": "",
+ "search.filters.summaryLabel.user": "",
"search.filters.title": "",
"search.filters.topic": "",
"search.filters.useSpatialFilter": "",
diff --git a/translations/fr.json b/translations/fr.json
index 8a6b021ad..a0283ff2d 100644
--- a/translations/fr.json
+++ b/translations/fr.json
@@ -527,6 +527,8 @@
"search.filters.representationType": "Type de représentation",
"search.filters.resourceType": "Type de ressource",
"search.filters.standard": "Standard",
+ "search.filters.summaryLabel.changeDate": "Modifiée le : ",
+ "search.filters.summaryLabel.user": "Modifiée par : ",
"search.filters.title": "Affiner votre recherche",
"search.filters.topic": "Thèmes",
"search.filters.useSpatialFilter": "Mettre en avant les résultats sur la zone d'intérêt",
diff --git a/translations/it.json b/translations/it.json
index 445ca702d..9d63219f3 100644
--- a/translations/it.json
+++ b/translations/it.json
@@ -527,6 +527,8 @@
"search.filters.representationType": "Tipo di rappresentazione",
"search.filters.resourceType": "Tipo di risorsa",
"search.filters.standard": "Standard",
+ "search.filters.summaryLabel.changeDate": "",
+ "search.filters.summaryLabel.user": "",
"search.filters.title": "Affina la sua ricerca",
"search.filters.topic": "Argomenti",
"search.filters.useSpatialFilter": "Evidenzia i risultati nell'area di interesse",
diff --git a/translations/nl.json b/translations/nl.json
index b620c75c8..e51a598a9 100644
--- a/translations/nl.json
+++ b/translations/nl.json
@@ -527,6 +527,8 @@
"search.filters.representationType": "",
"search.filters.resourceType": "",
"search.filters.standard": "",
+ "search.filters.summaryLabel.changeDate": "",
+ "search.filters.summaryLabel.user": "",
"search.filters.title": "",
"search.filters.topic": "",
"search.filters.useSpatialFilter": "",
diff --git a/translations/pt.json b/translations/pt.json
index a532a8a84..cfe9539f1 100644
--- a/translations/pt.json
+++ b/translations/pt.json
@@ -527,6 +527,8 @@
"search.filters.representationType": "",
"search.filters.resourceType": "",
"search.filters.standard": "",
+ "search.filters.summaryLabel.changeDate": "",
+ "search.filters.summaryLabel.user": "",
"search.filters.title": "",
"search.filters.topic": "",
"search.filters.useSpatialFilter": "",
diff --git a/translations/sk.json b/translations/sk.json
index 088c2d708..b95f54ffd 100644
--- a/translations/sk.json
+++ b/translations/sk.json
@@ -527,6 +527,8 @@
"search.filters.representationType": "Typ reprezentácie",
"search.filters.resourceType": "Typ zdroja",
"search.filters.standard": "Štandard",
+ "search.filters.summaryLabel.changeDate": "",
+ "search.filters.summaryLabel.user": "",
"search.filters.title": "Filtrovanie výsledkov",
"search.filters.topic": "Témy",
"search.filters.useSpatialFilter": "Najskôr zobraziť záznamy v oblasti záujmu",