diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000..1dd178b2b8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,49 @@ +--- +name: Bug report +about: Create a report to help us improve GeoNetwork-UI +title: '' +labels: 'bug' +assignees: '' +--- + +### Describe the bug + +When opening the application [...] + + + +- GeoNetwork-UI version used: [`X.X.X` or `main`] + +- Application: [...] + + + +### Expected behaviour + +The application should [...] + + + +### Steps To Reproduce + +- Run [...] +- Open [...] +- Click on [...] + + + +### Screenshots + +[...] + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000..d5ad669ad0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,33 @@ +--- +name: Feature request +about: Suggest an idea or request a feature for this project +title: '' +labels: 'feature request' +assignees: +--- + +### Description + +I would like to request adding [...] + + + +### Motivation + +This request stems from the fact that [...] + + + +### Inspiration and context + +This request takes inspiration from [...] + + diff --git a/.github/ISSUE_TEMPLATE/proposal.md b/.github/ISSUE_TEMPLATE/proposal.md new file mode 100644 index 0000000000..bf1980d60b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/proposal.md @@ -0,0 +1,57 @@ +--- +name: Proposal +about: Describe a feature or tech task that you intend to push forward +title: '' +labels: 'enhancement' +assignees: '' +--- + +### Description of the proposal + +This proposal aims at adding [...] + +The final outcome will be [...] + + + +### Motivation + +This proposal stems from the fact that [...] + + + +### Scope + +This proposal affects [...] + + + +### Possible alternatives + +Another way of achieving this could be to [...] + + + +### Inspiration and context + +This proposal takes inspiration from [...] + + + +--- + + + +**This work is sponsored by [Organization ABC](xx)**. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..4420beff93 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,45 @@ +### Description + +This PR introduces [...] + + + +### Architectural changes + +The following library now depends on [...] + + + +### Screenshots + +[...] + + + +### Quality Assurance Checklist + +- [ ] Commit history is devoid of any _merge commits_ and readable to facilitate reviews +- [ ] If **new logic** ⚙️ is introduced: unit tests were added +- [ ] If **new user stories** 🤏 are introduced: E2E tests were added +- [ ] If **new UI components** 🕹️ are introduced: corresponding stories in Storybook were created +- [ ] If **breaking changes** 🪚 are introduced: add the `breaking change` label +- [ ] If **bugs** 🐞 are fixed: add the `backport ` label +- [ ] The [documentation website](docs) 📚 has received the love it deserves + + + +--- + + + +**This work is sponsored by [Organization ABC](xx)**. diff --git a/apps/datahub-e2e/src/e2e/datasetDetailPage.cy.ts b/apps/datahub-e2e/src/e2e/datasetDetailPage.cy.ts index 935afb3c83..e490683a5e 100644 --- a/apps/datahub-e2e/src/e2e/datasetDetailPage.cy.ts +++ b/apps/datahub-e2e/src/e2e/datasetDetailPage.cy.ts @@ -73,6 +73,15 @@ describe('dataset pages', () => { .children('header') .find('gn-ui-navigation-button') }) + it('should display the data type, last update and status', () => { + cy.visit('/dataset/01491630-78ce-49f3-b479-4b30dabc4c69') + cy.get('datahub-header-record') + .children('header') + .find('.font-title') + .next() + .as('infoBar') + cy.get('@infoBar').children('div').should('have.length', 3) + }) it('should return to the dataset list', () => { cy.get('datahub-header-record') .children('header') @@ -81,22 +90,6 @@ describe('dataset pages', () => { cy.url().should('include', '/search') }) }) - describe('navigation bar', () => { - it('should display the navigation bar with 4 sections', () => { - cy.get('datahub-navigation-bar') - .find('button') - .filter(':visible') - .should('have.length', 4) - }) - it('should scroll down/up to the clicked section', () => { - cy.get('datahub-navigation-bar') - .find('button') - .filter(':visible') - .as('navBtns') - cy.get('@navBtns').eq(3).click() - cy.get('datahub-record-otherlinks').should('be.visible') - }) - }) }) describe('ABOUT SECTION : display & functions', () => { @@ -104,17 +97,26 @@ describe('dataset pages', () => { it('should display the description', () => { cy.get('datahub-record-metadata') .find('[id="about"]') - .find('gn-ui-metadata-info') - .find('gn-ui-content-ghost') - .find('gn-ui-max-lines') - .children('div') - .children('div') - .children('p') + .find('gn-ui-markdown-parser') .should(($element) => { const text = $element.text().trim() expect(text).not.to.equal('') }) }) + it.only('should display the thumbnail image and magnify', () => { + cy.get('datahub-record-metadata') + .find('[id="about"]') + .find('gn-ui-image-overlay-preview') + .as('overlay') + .should('have.length', 1) + cy.get('@overlay').find('gn-ui-button').click() + cy.get('[class="basicLightbox__placeholder"]') + .as('lightbox') + .find('img') + .should('have.length', 1) + cy.get('body').click() + cy.get('@lightbox').should('have.length', 0) + }) it('should display the contact details', () => { cy.get('datahub-record-metadata') .find('[id="about"]') @@ -141,16 +143,8 @@ describe('dataset pages', () => { }) }) it('should display the keywords', () => { - cy.get('datahub-record-metadata') - .find('[id="about"]') - .find('gn-ui-metadata-info') - .find('gn-ui-content-ghost') - .find('gn-ui-max-lines') - .children('div') - .children('div') - .children('div') - .children('gn-ui-badge') - .should('have.length.gt', 0) + cy.get('gn-ui-expandable-panel').eq(2).click() + cy.get('gn-ui-badge').should('have.length.gt', 0) }) it('should display the lineage and usage tabs', () => { cy.get('datahub-record-metadata') @@ -185,20 +179,9 @@ describe('dataset pages', () => { cy.url().should('include', '/search?publisher=') }) it('should go to dataset search page when clicking on keyword and filter by keyword', () => { - cy.get('datahub-record-metadata') - .find('[id="about"]') - .find('gn-ui-metadata-info') - .find('gn-ui-content-ghost') - .find('gn-ui-max-lines') - .children('div') - .contains('Read more') - .click() + cy.get('gn-ui-expandable-panel').eq(2).click() - cy.get('datahub-record-metadata') - .find('gn-ui-badge') - .children('div') - .first() - .as('keyword') + cy.get('gn-ui-badge').should('have.length.gt', 0).eq(1).as('keyword') cy.get('@keyword').then((key) => { keyword = key.text().toUpperCase() diff --git a/apps/datahub/project.json b/apps/datahub/project.json index aa1c97bd2d..7d3ed4e6a4 100644 --- a/apps/datahub/project.json +++ b/apps/datahub/project.json @@ -31,6 +31,7 @@ "tailwind.base.css", "apps/datahub/src/styles.css", "node_modules/tippy.js/dist/tippy.css", + "node_modules/basiclightbox/dist/basicLightbox.min.css", "node_modules/ol/ol.css", "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css" ], diff --git a/apps/datahub/src/app/record/header-record/header-record.component.html b/apps/datahub/src/app/record/header-record/header-record.component.html index f20951d6de..7cf866be08 100644 --- a/apps/datahub/src/app/record/header-record/header-record.component.html +++ b/apps/datahub/src/app/record/header-record/header-record.component.html @@ -1,36 +1,62 @@ -
+
- +
+
+ + +
+
+ + +
+
{{ metadata.title }}
- -
- +
- + + my_location + +

record.metadata.type

+
+
+

record.metadata.lastUpdate

+

+ {{ lastUpdate }} +

+
+
+

+

+
- -
- -
diff --git a/apps/datahub/src/app/record/header-record/header-record.component.spec.ts b/apps/datahub/src/app/record/header-record/header-record.component.spec.ts index 6428cfa72e..69c0334ddb 100644 --- a/apps/datahub/src/app/record/header-record/header-record.component.spec.ts +++ b/apps/datahub/src/app/record/header-record/header-record.component.spec.ts @@ -1,7 +1,10 @@ import { NO_ERRORS_SCHEMA } from '@angular/core' import { ComponentFixture, TestBed } from '@angular/core/testing' +import { DATASET_RECORDS } from '@geonetwork-ui/common/fixtures' +import { MdViewFacade } from '@geonetwork-ui/feature/record' import { SearchService } from '@geonetwork-ui/feature/search' import { TranslateModule } from '@ngx-translate/core' +import { BehaviorSubject } from 'rxjs' import { HeaderRecordComponent } from './header-record.component' @@ -10,11 +13,22 @@ jest.mock('@geonetwork-ui/util/app-config', () => ({ HEADER_BACKGROUND: 'red', HEADER_FOREGROUND_COLOR: 'white', }), + getGlobalConfig() { + return { + LANGUAGES: ['en', 'es'], + } + }, })) const searchServiceMock = { updateFilters: jest.fn(), } + +class MdViewFacadeMock { + mapApiLinks$ = new BehaviorSubject([]) + geoDataLinks$ = new BehaviorSubject([]) +} + describe('HeaderRecordComponent', () => { let component: HeaderRecordComponent let fixture: ComponentFixture @@ -24,13 +38,22 @@ describe('HeaderRecordComponent', () => { declarations: [HeaderRecordComponent], imports: [TranslateModule.forRoot()], schemas: [NO_ERRORS_SCHEMA], - providers: [{ provide: SearchService, useValue: searchServiceMock }], + providers: [ + { provide: SearchService, useValue: searchServiceMock }, + { + provide: MdViewFacade, + useClass: MdViewFacadeMock, + }, + ], }).compileComponents() }) beforeEach(() => { fixture = TestBed.createComponent(HeaderRecordComponent) component = fixture.componentInstance + component.metadata = { + ...DATASET_RECORDS[0], + } fixture.detectChanges() }) diff --git a/apps/datahub/src/app/record/header-record/header-record.component.ts b/apps/datahub/src/app/record/header-record/header-record.component.ts index 248db72c3d..9d92672674 100644 --- a/apps/datahub/src/app/record/header-record/header-record.component.ts +++ b/apps/datahub/src/app/record/header-record/header-record.component.ts @@ -1,7 +1,10 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core' import { SearchService } from '@geonetwork-ui/feature/search' -import { getThemeConfig } from '@geonetwork-ui/util/app-config' -import { CatalogRecord } from '@geonetwork-ui/common/domain/model/record' +import { getGlobalConfig, getThemeConfig } from '@geonetwork-ui/util/app-config' +import { DatasetRecord } from '@geonetwork-ui/common/domain/model/record' +import { MdViewFacade } from '@geonetwork-ui/feature/record' +import { combineLatest, map } from 'rxjs' +import { TranslateService } from '@ngx-translate/core' @Component({ selector: 'datahub-header-record', @@ -10,13 +13,34 @@ import { CatalogRecord } from '@geonetwork-ui/common/domain/model/record' changeDetection: ChangeDetectionStrategy.OnPush, }) export class HeaderRecordComponent { - @Input() metadata: CatalogRecord + @Input() metadata: DatasetRecord backgroundCss = getThemeConfig().HEADER_BACKGROUND || `center /cover url('assets/img/header_bg.webp')` foregroundColor = getThemeConfig().HEADER_FOREGROUND_COLOR || '#ffffff' + showLanguageSwitcher = getGlobalConfig().LANGUAGES?.length > 0 - constructor(private searchService: SearchService) {} + constructor( + private searchService: SearchService, + public facade: MdViewFacade, + private translateService: TranslateService + ) {} + + isGeodata$ = combineLatest([ + this.facade.mapApiLinks$, + this.facade.geoDataLinks$, + ]).pipe( + map( + ([mapLinks, geoDataLinks]) => + mapLinks?.length > 0 || geoDataLinks?.length > 0 + ) + ) + + get lastUpdate() { + return this.metadata.recordUpdated.toLocaleDateString( + this.translateService.currentLang + ) + } back() { this.searchService.updateFilters({}) diff --git a/apps/datahub/src/app/record/record-metadata/record-metadata.component.html b/apps/datahub/src/app/record/record-metadata/record-metadata.component.html index c07d56fccc..c4e7a0fcb1 100644 --- a/apps/datahub/src/app/record/record-metadata/record-metadata.component.html +++ b/apps/datahub/src/app/record/record-metadata/record-metadata.component.html @@ -33,13 +33,21 @@ [metadataQualityDisplay]="metadataQualityDisplay" > - - - - + +
+ + + + +
diff --git a/apps/datahub/src/app/record/record-metadata/record-metadata.component.spec.ts b/apps/datahub/src/app/record/record-metadata/record-metadata.component.spec.ts index 4e449bfc01..35243aa0ae 100644 --- a/apps/datahub/src/app/record/record-metadata/record-metadata.component.spec.ts +++ b/apps/datahub/src/app/record/record-metadata/record-metadata.component.spec.ts @@ -148,6 +148,15 @@ export class MockMetadataCatalogComponent { export class MockRecordApiFormComponent { @Input() apiLink: DatasetServiceDistribution } +@Component({ + // eslint-disable-next-line @angular-eslint/component-selector + selector: 'gn-ui-image-overlay-preview', + template: '
', +}) +export class MockImgOverlayPreviewComponent { + @Input() imageUrl: string + @Output() isPlaceholderShown = new EventEmitter() +} describe('RecordMetadataComponent', () => { let component: RecordMetadataComponent @@ -172,6 +181,7 @@ describe('RecordMetadataComponent', () => { MockMetadataCatalogComponent, MockMetadataContactComponent, MockRecordApiFormComponent, + MockImgOverlayPreviewComponent, ], schemas: [NO_ERRORS_SCHEMA], imports: [TranslateModule.forRoot()], @@ -267,6 +277,56 @@ describe('RecordMetadataComponent', () => { fixture.debugElement.query(By.directive(MockMetadataCatalogComponent)) ).toBeFalsy() }) + it('does not display the image overlay preview', () => { + expect( + fixture.debugElement.query( + By.directive(MockImgOverlayPreviewComponent) + ) + ).toBeFalsy() + }) + }) + describe('Image Overlay Preview', () => { + describe('if metadata without overview', () => { + let imgOverlayPreview: MockImgOverlayPreviewComponent + beforeEach(() => { + facade.isPresent$.next(true) + facade.metadata$.next({}) + fixture.detectChanges() + imgOverlayPreview = fixture.debugElement.query( + By.directive(MockImgOverlayPreviewComponent) + ).componentInstance + }) + it('should send undefined as imageUrl to imgOverlayPreview component', () => { + expect(imgOverlayPreview).toBeTruthy() + expect(imgOverlayPreview.imageUrl).toBe(undefined) + }) + }) + describe('if metadata with overview', () => { + let imgOverlayPreview: MockImgOverlayPreviewComponent + beforeEach(() => { + facade.isPresent$.next(true) + fixture.detectChanges() + imgOverlayPreview = fixture.debugElement.query( + By.directive(MockImgOverlayPreviewComponent) + ).componentInstance + }) + describe('and url defined', () => { + it('should send the imageUrl to imgOverlayPreview component', () => { + expect(imgOverlayPreview).toBeTruthy() + expect(imgOverlayPreview.imageUrl).toBeDefined() + }) + }) + describe('and url undefined', () => { + beforeEach(() => { + facade.metadata$.next({ overviews: [] }) + fixture.detectChanges() + }) + it('should send the imagUrl as null to imgOverlayPreview component', () => { + expect(imgOverlayPreview).toBeTruthy() + expect(imgOverlayPreview.imageUrl).toBeNull() + }) + }) + }) }) }) diff --git a/apps/datahub/src/app/record/record-metadata/record-metadata.component.ts b/apps/datahub/src/app/record/record-metadata/record-metadata.component.ts index ac9d0aeabe..58d718ee5d 100644 --- a/apps/datahub/src/app/record/record-metadata/record-metadata.component.ts +++ b/apps/datahub/src/app/record/record-metadata/record-metadata.component.ts @@ -55,6 +55,21 @@ export class RecordMetadataComponent { errorTypes = ErrorType selectedTabIndex$ = new BehaviorSubject(0) + thumbnailUrl$ = this.facade.metadata$.pipe( + map((metadata) => { + // in order to differentiate between metadata not loaded yet + // and url not defined + // the content-ghost of image-overlay-preview relies on this differentiation + if (metadata?.overviews === undefined) { + return undefined + } else { + return metadata?.overviews?.[0]?.url ?? null + } + }) + ) + + showOverlay = true + constructor( public facade: MdViewFacade, private searchService: SearchService, diff --git a/apps/webcomponents/src/app/app.component.html b/apps/webcomponents/src/app/app.component.html index f6c2bdcfaf..88454b96bc 100644 --- a/apps/webcomponents/src/app/app.component.html +++ b/apps/webcomponents/src/app/app.component.html @@ -1,7 +1,7 @@
diff --git a/apps/webcomponents/src/app/components/gn-dataset-view-chart/gn-dataset-view-chart.sample.html b/apps/webcomponents/src/app/components/gn-dataset-view-chart/gn-dataset-view-chart.sample.html index aa88dcf175..c98e024c64 100644 --- a/apps/webcomponents/src/app/components/gn-dataset-view-chart/gn-dataset-view-chart.sample.html +++ b/apps/webcomponents/src/app/components/gn-dataset-view-chart/gn-dataset-view-chart.sample.html @@ -39,7 +39,7 @@

SCoT

SCoT

Vélo

{ useLimitations: [], spatialExtents: [], temporalExtents: [], + overviews: [], } const record: CatalogRecord = Object.keys(_source).reduce( (prev, fieldName) => diff --git a/libs/feature/map/src/lib/add-layer-from-file/add-layer-from-file.component.ts b/libs/feature/map/src/lib/add-layer-from-file/add-layer-from-file.component.ts index f7ea957150..4db2046f41 100644 --- a/libs/feature/map/src/lib/add-layer-from-file/add-layer-from-file.component.ts +++ b/libs/feature/map/src/lib/add-layer-from-file/add-layer-from-file.component.ts @@ -99,11 +99,8 @@ export class AddLayerFromFileComponent { } setTimeout(() => { - if (type === 'success') { - this.successMessage = null - } else if (type === 'error') { - this.errorMessage = null - } + this.successMessage = null + this.errorMessage = null this.changeDetectorRef.detectChanges() }, 5000) } diff --git a/libs/feature/map/src/lib/add-layer-from-wfs/add-layer-from-wfs.component.css b/libs/feature/map/src/lib/add-layer-from-wfs/add-layer-from-wfs.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/libs/feature/map/src/lib/add-layer-from-wfs/add-layer-from-wfs.component.html b/libs/feature/map/src/lib/add-layer-from-wfs/add-layer-from-wfs.component.html new file mode 100644 index 0000000000..a022248950 --- /dev/null +++ b/libs/feature/map/src/lib/add-layer-from-wfs/add-layer-from-wfs.component.html @@ -0,0 +1,37 @@ +
+ + +
+ +
+ {{ errorMessage }} +
+ +
+

map.loading.service

+
+ +
+

map.layers.available

+ +
+

+ {{ layer.title }} +

+ map.layer.add +
+
+
diff --git a/libs/feature/map/src/lib/add-layer-from-wfs/add-layer-from-wfs.component.spec.ts b/libs/feature/map/src/lib/add-layer-from-wfs/add-layer-from-wfs.component.spec.ts new file mode 100644 index 0000000000..e11cf446e9 --- /dev/null +++ b/libs/feature/map/src/lib/add-layer-from-wfs/add-layer-from-wfs.component.spec.ts @@ -0,0 +1,165 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing' +import { AddLayerFromWfsComponent } from './add-layer-from-wfs.component' +import { MapFacade } from '../+state/map.facade' +import { TranslateModule } from '@ngx-translate/core' +import { By } from '@angular/platform-browser' + +jest.mock('@camptocamp/ogc-client', () => ({ + WfsEndpoint: class { + constructor(private url) {} + isReady() { + if (this.url.indexOf('error') > -1) { + return Promise.reject(new Error('Something went wrong')) + } + if (this.url.indexOf('wait') > -1) { + return new Promise(() => { + // do nothing + }) + } + return Promise.resolve(this) + } + getFeatureTypes() { + return [ + { + name: 'ft1', + title: 'Feature Type 1', + }, + { + name: 'ft2', + title: 'Feature Type 2', + }, + { + name: 'ft3', + title: 'Feature Type 3', + }, + ] + } + }, +})) + +class MapFacadeMock { + addLayer = jest.fn() +} + +describe('AddLayerFromWfsComponent', () => { + let component: AddLayerFromWfsComponent + let fixture: ComponentFixture + let mapFacade: MapFacade + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot()], + declarations: [AddLayerFromWfsComponent], + providers: [ + { + provide: MapFacade, + useClass: MapFacadeMock, + }, + ], + }).compileComponents() + + mapFacade = TestBed.inject(MapFacade) + fixture = TestBed.createComponent(AddLayerFromWfsComponent) + component = fixture.componentInstance + }) + + it('should create', () => { + fixture.detectChanges() + expect(component).toBeTruthy() + expect(component.errorMessage).toBeFalsy() + expect(component.loading).toBe(false) + expect(component.layers.length).toBe(0) + }) + + describe('loadLayers', () => { + describe('while layers are loading', () => { + beforeEach(() => { + component.wfsUrl = 'http://my.service.org/wait' + component.loadLayers() + }) + it('shows only a "loading" message', () => { + expect(component.errorMessage).toBeFalsy() + expect(component.loading).toBe(true) + expect(component.layers).toEqual([]) + }) + }) + describe('valid WFS service', () => { + beforeEach(() => { + component.wfsUrl = 'http://my.service.org/wfs' + component.loadLayers() + }) + it('shows all layers', () => { + expect(component.errorMessage).toBeFalsy() + expect(component.loading).toBe(false) + expect(component.layers).toEqual([ + { + name: 'ft1', + title: 'Feature Type 1', + }, + { + name: 'ft2', + title: 'Feature Type 2', + }, + { + name: 'ft3', + title: 'Feature Type 3', + }, + ]) + }) + it('should show a Add button for each layer', () => { + fixture.detectChanges() + const layerElts = fixture.debugElement.queryAll( + By.css('.layer-item-tree') + ) + expect(layerElts.length).toBe(3) + const hasButtons = layerElts.map( + (layerElt) => !!layerElt.query(By.css('.layer-add-btn')) + ) + expect(hasButtons).toEqual([true, true, true]) + }) + }) + describe('error loading layers', () => { + beforeEach(() => { + component.wfsUrl = 'http://my.service.org/error' + component.loadLayers() + }) + it('shows an error message', () => { + expect(component.errorMessage).toBeTruthy() + expect(component.loading).toBe(false) + expect(component.layers.length).toBe(0) + }) + }) + describe('error and then valid service', () => { + beforeEach(async () => { + component.wfsUrl = 'http://my.service.org/error' + await component.loadLayers().catch(() => { + // do nothing + }) + component.wfsUrl = 'http://my.service.org/wfs' + await component.loadLayers() + }) + it('shows no error', () => { + expect(component.errorMessage).toBeFalsy() + expect(component.loading).toBe(false) + expect(component.layers).not.toEqual([]) + }) + }) + }) + describe('addLayer', () => { + beforeEach(() => { + component.wfsUrl = 'http://my.service.org/wfs' + component.addLayer({ + name: 'ft1', + title: 'Feature Type 1', + }) + }) + it('should add the selected layer in the current map context', () => { + expect(mapFacade.addLayer).toHaveBeenCalledWith({ + name: 'ft1', + title: 'Feature Type 1', + url: 'http://my.service.org/wfs', + type: 'wfs', + }) + }) + }) +}) diff --git a/libs/feature/map/src/lib/add-layer-from-wfs/add-layer-from-wfs.component.ts b/libs/feature/map/src/lib/add-layer-from-wfs/add-layer-from-wfs.component.ts new file mode 100644 index 0000000000..a3ec798089 --- /dev/null +++ b/libs/feature/map/src/lib/add-layer-from-wfs/add-layer-from-wfs.component.ts @@ -0,0 +1,64 @@ +import { ChangeDetectorRef, Component, OnInit } from '@angular/core' +import { WfsEndpoint, WfsFeatureTypeBrief } from '@camptocamp/ogc-client' +import { Subject } from 'rxjs' +import { + MapContextLayerModel, + MapContextLayerTypeEnum, +} from '../map-context/map-context.model' +import { MapFacade } from '../+state/map.facade' +import { debounceTime } from 'rxjs/operators' + +@Component({ + selector: 'gn-ui-add-layer-from-wfs', + templateUrl: './add-layer-from-wfs.component.html', + styleUrls: ['./add-layer-from-wfs.component.css'], +}) +export class AddLayerFromWfsComponent implements OnInit { + wfsUrl = '' + loading = false + layers: WfsFeatureTypeBrief[] = [] + wfsEndpoint: WfsEndpoint | null = null + urlChange = new Subject() + errorMessage: string | null = null + + constructor( + private mapFacade: MapFacade, + private changeDetectorRef: ChangeDetectorRef + ) {} + + ngOnInit() { + this.urlChange.pipe(debounceTime(700)).subscribe(() => this.loadLayers()) + } + + async loadLayers() { + this.errorMessage = null + try { + this.loading = true + + if (this.wfsUrl.trim() === '') { + this.layers = [] + return + } + + this.wfsEndpoint = await new WfsEndpoint(this.wfsUrl).isReady() + this.layers = this.wfsEndpoint.getFeatureTypes() + console.log(this.layers) + } catch (error) { + const err = error as Error + this.layers = [] + this.errorMessage = 'Error loading layers: ' + err.message + } finally { + this.loading = false + this.changeDetectorRef.markForCheck() + } + } + + addLayer(layer: WfsFeatureTypeBrief) { + const layerToAdd: MapContextLayerModel = { + name: layer.name, + url: this.wfsUrl.toString(), + type: MapContextLayerTypeEnum.WFS, + } + this.mapFacade.addLayer({ ...layerToAdd, title: layer.title }) + } +} diff --git a/libs/feature/map/src/lib/feature-map.module.ts b/libs/feature/map/src/lib/feature-map.module.ts index 5737dee3e9..c4d5f7c333 100644 --- a/libs/feature/map/src/lib/feature-map.module.ts +++ b/libs/feature/map/src/lib/feature-map.module.ts @@ -22,6 +22,7 @@ import { UiElementsModule } from '@geonetwork-ui/ui/elements' import { UiInputsModule } from '@geonetwork-ui/ui/inputs' import { AddLayerFromWmsComponent } from './add-layer-from-wms/add-layer-from-wms.component' import { AddLayerFromFileComponent } from './add-layer-from-file/add-layer-from-file.component' +import { AddLayerFromWfsComponent } from './add-layer-from-wfs/add-layer-from-wfs.component' @NgModule({ declarations: [ @@ -33,6 +34,7 @@ import { AddLayerFromFileComponent } from './add-layer-from-file/add-layer-from- AddLayerRecordPreviewComponent, AddLayerFromWmsComponent, AddLayerFromFileComponent, + AddLayerFromWfsComponent, ], exports: [ MapContextComponent, diff --git a/libs/feature/map/src/lib/layers-panel/layers-panel.component.html b/libs/feature/map/src/lib/layers-panel/layers-panel.component.html index 0cf4df0112..199dcaa606 100644 --- a/libs/feature/map/src/lib/layers-panel/layers-panel.component.html +++ b/libs/feature/map/src/lib/layers-panel/layers-panel.component.html @@ -36,7 +36,9 @@ -
Add from WFS
+
+ +
diff --git a/libs/feature/map/src/lib/map-context/map-context.service.spec.ts b/libs/feature/map/src/lib/map-context/map-context.service.spec.ts index 0bc5494d30..8862473d28 100644 --- a/libs/feature/map/src/lib/map-context/map-context.service.spec.ts +++ b/libs/feature/map/src/lib/map-context/map-context.service.spec.ts @@ -137,7 +137,7 @@ describe('MapContextService', () => { const source = layer.getSource() const urlLoader = source.getUrl() expect(urlLoader([10, 20, 30, 40])).toBe( - 'https://www.geograndest.fr/geoserver/region-grand-est/ows?service=WFS&version=1.1.0&request=GetFeature&outputFormat=application%2Fjson&typename=ms%3Acommune_actuelle_3857&srsname=EPSG%3A3857&bbox=10%2C20%2C30%2C40%2CEPSG%3A3857' + 'https://www.geograndest.fr/geoserver/region-grand-est/ows?service=WFS&version=1.1.0&request=GetFeature&outputFormat=application%2Fjson&typename=ms%3Acommune_actuelle_3857&srsname=EPSG%3A3857&bbox=10%2C20%2C30%2C40%2CEPSG%3A3857&maxFeatures=10000' ) }) }) diff --git a/libs/feature/map/src/lib/map-context/map-context.service.ts b/libs/feature/map/src/lib/map-context/map-context.service.ts index b717585757..8f8c316c31 100644 --- a/libs/feature/map/src/lib/map-context/map-context.service.ts +++ b/libs/feature/map/src/lib/map-context/map-context.service.ts @@ -40,6 +40,8 @@ export const DEFAULT_VIEW: MapContextViewModel = { zoom: 2, } +export const WFS_MAX_FEATURES = 10000 + @Injectable({ providedIn: 'root', }) @@ -111,6 +113,10 @@ export class MapContextService { urlObj.searchParams.set('typename', layerModel.name) urlObj.searchParams.set('srsname', 'EPSG:3857') urlObj.searchParams.set('bbox', `${extent.join(',')},EPSG:3857`) + urlObj.searchParams.set( + 'maxFeatures', + WFS_MAX_FEATURES.toString() + ) return urlObj.toString() }, strategy: bboxStrategy, diff --git a/libs/feature/search/src/lib/favorites/favorite-star/favorite-star.component.html b/libs/feature/search/src/lib/favorites/favorite-star/favorite-star.component.html index 401e9b1ee2..aff63e96d6 100644 --- a/libs/feature/search/src/lib/favorites/favorite-star/favorite-star.component.html +++ b/libs/feature/search/src/lib/favorites/favorite-star/favorite-star.component.html @@ -1,10 +1,18 @@ -
- {{ - favoriteCount - }} +
+ {{ favoriteCount }} +
diff --git a/libs/feature/search/src/lib/favorites/favorite-star/favorite-star.component.spec.ts b/libs/feature/search/src/lib/favorites/favorite-star/favorite-star.component.spec.ts index b360a4b617..b2d3272a07 100644 --- a/libs/feature/search/src/lib/favorites/favorite-star/favorite-star.component.spec.ts +++ b/libs/feature/search/src/lib/favorites/favorite-star/favorite-star.component.spec.ts @@ -66,6 +66,7 @@ describe('FavoriteStarComponent', () => { favoritesService = TestBed.inject(FavoritesService) fixture = TestBed.createComponent(FavoriteStarComponent) component = fixture.componentInstance + component.displayCount = 'true' fixture.detectChanges() starToggle = fixture.debugElement.query( By.directive(StarToggleComponent) diff --git a/libs/feature/search/src/lib/favorites/favorite-star/favorite-star.component.ts b/libs/feature/search/src/lib/favorites/favorite-star/favorite-star.component.ts index 2e980bc15e..7dc7e6a6f4 100644 --- a/libs/feature/search/src/lib/favorites/favorite-star/favorite-star.component.ts +++ b/libs/feature/search/src/lib/favorites/favorite-star/favorite-star.component.ts @@ -24,6 +24,7 @@ import { PlatformServiceInterface } from '@geonetwork-ui/common/domain/platform. changeDetection: ChangeDetectionStrategy.OnPush, }) export class FavoriteStarComponent implements AfterViewInit, OnDestroy { + @Input() displayCount? = true @Input() set record(value) { this.record_ = value this.favoriteCount = diff --git a/libs/ui/catalog/src/lib/language-switcher/language-switcher.component.html b/libs/ui/catalog/src/lib/language-switcher/language-switcher.component.html index 70f8b1bb58..6922b307ad 100644 --- a/libs/ui/catalog/src/lib/language-switcher/language-switcher.component.html +++ b/libs/ui/catalog/src/lib/language-switcher/language-switcher.component.html @@ -5,5 +5,6 @@ [selected]="currentLang" ariaName="languages" [showTitle]="false" + [extraBtnClass]="'flex justify-items-center text-white !pl-2 !py-1'" > diff --git a/libs/ui/catalog/src/lib/language-switcher/language-switcher.component.ts b/libs/ui/catalog/src/lib/language-switcher/language-switcher.component.ts index aa631a93ec..ad781c73cf 100644 --- a/libs/ui/catalog/src/lib/language-switcher/language-switcher.component.ts +++ b/libs/ui/catalog/src/lib/language-switcher/language-switcher.component.ts @@ -34,7 +34,7 @@ export class LanguageSwitcherComponent implements OnInit { ngOnInit(): void { const languages = this.languagePlaceholder || DEFAULT_LANGUAGES this.languageList = languages.map((language) => ({ - label: `language.${language}`, + label: `${language}`.toUpperCase(), value: language, })) } diff --git a/libs/ui/elements/src/index.ts b/libs/ui/elements/src/index.ts index ed3b7f3ad5..a0ee32142f 100644 --- a/libs/ui/elements/src/index.ts +++ b/libs/ui/elements/src/index.ts @@ -20,3 +20,4 @@ export * from './lib/search-results-error/search-results-error.component' export * from './lib/user-preview/user-preview.component' export * from './lib/record-api-form/record-api-form.component' export * from './lib/markdown-parser/markdown-parser.component' +export * from './lib/image-overlay-preview/image-overlay-preview.component' diff --git a/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.css b/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.html b/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.html new file mode 100644 index 0000000000..024d2bdbfa --- /dev/null +++ b/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.html @@ -0,0 +1,29 @@ + +
+ +
+ + zoom_out_map + +
+
+
diff --git a/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.spec.ts b/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.spec.ts new file mode 100644 index 0000000000..2e4c996979 --- /dev/null +++ b/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing' + +import { ImageOverlayPreviewComponent } from './image-overlay-preview.component' + +describe('ImageOverlayPreviewComponent', () => { + let component: ImageOverlayPreviewComponent + let fixture: ComponentFixture + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [ImageOverlayPreviewComponent], + }) + fixture = TestBed.createComponent(ImageOverlayPreviewComponent) + component = fixture.componentInstance + fixture.detectChanges() + }) + + it('should create', () => { + expect(component).toBeTruthy() + }) +}) diff --git a/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.ts b/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.ts new file mode 100644 index 0000000000..c7f24db928 --- /dev/null +++ b/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.ts @@ -0,0 +1,15 @@ +import { Component, EventEmitter, Input, Output } from '@angular/core' +import * as basicLightbox from 'basiclightbox' + +@Component({ + selector: 'gn-ui-image-overlay-preview', + templateUrl: './image-overlay-preview.component.html', + styleUrls: ['./image-overlay-preview.component.css'], +}) +export class ImageOverlayPreviewComponent { + @Input() imageUrl: string + @Output() isPlaceholderShown = new EventEmitter() + openLightbox(src: string) { + basicLightbox.create(``).show() + } +} diff --git a/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.html b/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.html index 6670ab21ec..804dfe6a5e 100644 --- a/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.html +++ b/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.html @@ -20,7 +20,7 @@ (click)="onOrganizationClick()" data-cy="organization-name" > - {{ shownOrganization.name }} + {{ shownOrganization?.name }}
diff --git a/libs/ui/elements/src/lib/metadata-info/metadata-info.component.css b/libs/ui/elements/src/lib/metadata-info/metadata-info.component.css index e7f085d517..3b22c90eaa 100644 --- a/libs/ui/elements/src/lib/metadata-info/metadata-info.component.css +++ b/libs/ui/elements/src/lib/metadata-info/metadata-info.component.css @@ -7,3 +7,7 @@ padding-bottom: 10px; @apply border-b border-gray-300; } + +:host ::ng-deep gn-ui-copy-text-button button mat-icon { + transform: scale(0.8); +} diff --git a/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html b/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html index fa13aae0ba..1a7be4c4bf 100644 --- a/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html +++ b/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html @@ -1,9 +1,3 @@ -

- record.metadata.about -

@@ -11,20 +5,6 @@ - -

- record.metadata.keywords -

-
- {{ keyword }} -
-
@@ -95,12 +75,61 @@ *ngIf="metadata.landingPage" [title]="'record.metadata.details' | translate" > -
-

record.metadata.sheet

-

- - {{ metadata.landingPage }} - -

+
+
+

record.metadata.updatedOn

+

+ {{ metadata.recordUpdated && metadata.recordUpdated.toLocaleString() }} +

+
+
+

record.metadata.sheet

+

+ + {{ metadata.landingPage }} + +

+
+
+

record.metadata.owner

+

+ {{ metadata.ownerOrganization.name }} +

+
+
+

record.metadata.uniqueId

+
+ +

+ {{ metadata.uniqueIdentifier }} +

+
+
+
+

record.metadata.themes

+
+ {{ theme }} +
+
+
+

record.metadata.keywords

+
+ {{ keyword }} +
+
diff --git a/libs/ui/elements/src/lib/metadata-info/metadata-info.component.spec.ts b/libs/ui/elements/src/lib/metadata-info/metadata-info.component.spec.ts index a1800f59e0..a8d302391c 100644 --- a/libs/ui/elements/src/lib/metadata-info/metadata-info.component.spec.ts +++ b/libs/ui/elements/src/lib/metadata-info/metadata-info.component.spec.ts @@ -73,17 +73,10 @@ describe('MetadataInfoComponent', () => { const displayedElement = fixture.nativeElement.querySelector('.noUsage') expect(displayedElement).toBeFalsy() }) - it('should display the keywords section', () => { - // Use waitForAsync to handle asynchronous changes in the DOM. - fixture.whenStable().then(() => { - const displayedElement = - fixture.nativeElement.querySelector('ng-container') - expect(displayedElement).toBeTruthy() - }) - }) it('should display the abstract section', () => { - const displayedElement = - fixture.nativeElement.querySelector('.md-description p') + const displayedElement = fixture.nativeElement.querySelector( + 'gn-ui-markdown-parser' + ) expect(displayedElement).toBeTruthy() }) it('should display the metadata origin even if there is no lineage text', () => { diff --git a/libs/ui/elements/src/lib/metadata-info/metadata-info.component.ts b/libs/ui/elements/src/lib/metadata-info/metadata-info.component.ts index b489a1913e..cee796092e 100644 --- a/libs/ui/elements/src/lib/metadata-info/metadata-info.component.ts +++ b/libs/ui/elements/src/lib/metadata-info/metadata-info.component.ts @@ -56,4 +56,9 @@ export class MetadataInfoComponent { onKeywordClick(keyword: string) { this.keyword.emit(keyword) } + + copyText() { + navigator.clipboard.writeText(this.metadata.uniqueIdentifier) + ;(event.target as HTMLElement).blur() + } } diff --git a/libs/ui/elements/src/lib/related-record-card/related-record-card.component.html b/libs/ui/elements/src/lib/related-record-card/related-record-card.component.html index b6fb95eba8..932c405b23 100644 --- a/libs/ui/elements/src/lib/related-record-card/related-record-card.component.html +++ b/libs/ui/elements/src/lib/related-record-card/related-record-card.component.html @@ -6,7 +6,7 @@
diff --git a/libs/ui/elements/src/lib/thumbnail/thumbnail.component.ts b/libs/ui/elements/src/lib/thumbnail/thumbnail.component.ts index be4e85eaf5..239dc4ca52 100644 --- a/libs/ui/elements/src/lib/thumbnail/thumbnail.component.ts +++ b/libs/ui/elements/src/lib/thumbnail/thumbnail.component.ts @@ -10,6 +10,8 @@ import { Optional, SimpleChanges, ViewChild, + Output, + EventEmitter, } from '@angular/core' export const THUMBNAIL_PLACEHOLDER = new InjectionToken( @@ -36,6 +38,7 @@ export class ThumbnailComponent implements OnInit, OnChanges { @Input() fit: FitOptions | FitOptions[] = 'cover' @ViewChild('imageElement') imgElement: ElementRef @ViewChild('containerElement') containerElement: ElementRef + @Output() placeholderShown = new EventEmitter() imgUrl: string imgFit: FitOptions placeholderUrl = this.optionalPlaceholderUrl || DEFAULT_PLACEHOLDER @@ -85,6 +88,7 @@ export class ThumbnailComponent implements OnInit, OnChanges { private setNewSrcImage(image: ThumbnailImageObject) { this.imgFit = image.fit this.imgUrl = image.url + this.placeholderShown.emit(this.isPlaceholder) } private setPlaceholder(): void { diff --git a/libs/ui/elements/src/lib/ui-elements.module.ts b/libs/ui/elements/src/lib/ui-elements.module.ts index eac4060e3b..0fd0093efa 100644 --- a/libs/ui/elements/src/lib/ui-elements.module.ts +++ b/libs/ui/elements/src/lib/ui-elements.module.ts @@ -30,6 +30,7 @@ import { PaginationButtonsComponent } from './pagination-buttons/pagination-butt import { MaxLinesComponent } from './max-lines/max-lines.component' import { RecordApiFormComponent } from './record-api-form/record-api-form.component' import { MarkdownParserComponent } from './markdown-parser/markdown-parser.component' +import { ImageOverlayPreviewComponent } from './image-overlay-preview/image-overlay-preview.component' @NgModule({ imports: [ @@ -67,6 +68,7 @@ import { MarkdownParserComponent } from './markdown-parser/markdown-parser.compo MaxLinesComponent, RecordApiFormComponent, MarkdownParserComponent, + ImageOverlayPreviewComponent, ], exports: [ MetadataInfoComponent, @@ -88,6 +90,7 @@ import { MarkdownParserComponent } from './markdown-parser/markdown-parser.compo PaginationButtonsComponent, RecordApiFormComponent, MarkdownParserComponent, + ImageOverlayPreviewComponent, ], }) export class UiElementsModule {} diff --git a/libs/ui/inputs/src/lib/navigation-button/navigation-button.component.html b/libs/ui/inputs/src/lib/navigation-button/navigation-button.component.html index c525d967ba..97789516cf 100644 --- a/libs/ui/inputs/src/lib/navigation-button/navigation-button.component.html +++ b/libs/ui/inputs/src/lib/navigation-button/navigation-button.component.html @@ -1,6 +1,11 @@ diff --git a/libs/ui/inputs/src/lib/navigation-button/navigation-button.component.spec.ts b/libs/ui/inputs/src/lib/navigation-button/navigation-button.component.spec.ts index 03f3864f13..81d9283773 100644 --- a/libs/ui/inputs/src/lib/navigation-button/navigation-button.component.spec.ts +++ b/libs/ui/inputs/src/lib/navigation-button/navigation-button.component.spec.ts @@ -17,6 +17,8 @@ describe('NavigationButtonComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(NavigationButtonComponent) component = fixture.componentInstance + component.icon = 'navigate_before' + component.label = 'Retours aux résultats' fixture.detectChanges() }) diff --git a/libs/ui/inputs/src/lib/star-toggle/star-toggle.component.css b/libs/ui/inputs/src/lib/star-toggle/star-toggle.component.css index 37815a4641..efc5d63ed2 100644 --- a/libs/ui/inputs/src/lib/star-toggle/star-toggle.component.css +++ b/libs/ui/inputs/src/lib/star-toggle/star-toggle.component.css @@ -11,6 +11,7 @@ mat-icon { font-size: 1.5em; margin-top: -0.1em; font-variation-settings: 'opsz' 40; + stroke: var(--color-primary-darker); } .star-filled { diff --git a/package-lock.json b/package-lock.json index e1d431a51d..065a5cf555 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,6 +40,7 @@ "@rgrove/parse-xml": "~4.0.1", "alasql": "^3.1.0", "axios": "^1.6.0", + "basiclightbox": "^5.0.4", "chart.js": "^4.2.0", "chroma-js": "^2.1.2", "cypress-browser-permissions": "^1.1.0", @@ -14708,6 +14709,11 @@ "node": ">= 0.8" } }, + "node_modules/basiclightbox": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/basiclightbox/-/basiclightbox-5.0.4.tgz", + "integrity": "sha512-EsuNWmfcFXWZOe0txKXsllYOC7bDpoaVLc4HHHlYKB/roymlZs+FBdLUU6rx2yPpnJZhulwheKdPjqr2k0+NGQ==" + }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", diff --git a/package.json b/package.json index 7c3bde2dd8..2287327f1f 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "@rgrove/parse-xml": "~4.0.1", "alasql": "^3.1.0", "axios": "^1.6.0", + "basiclightbox": "^5.0.4", "chart.js": "^4.2.0", "chroma-js": "^2.1.2", "cypress-browser-permissions": "^1.1.0", diff --git a/translations/de.json b/translations/de.json index 545022f641..14b8a4ce97 100644 --- a/translations/de.json +++ b/translations/de.json @@ -115,6 +115,7 @@ "datahub.news.contact.title": "", "datahub.news.feed": "Nachrichtenfeed", "datahub.news.figures": "Indikatoren", + "datahub.record.addToFavorites": "", "datahub.search.back": "Zurück zu den Ergebnissen", "datahub.search.filter.all": "Alle", "datahub.search.filter.generatedByWfs": "Generiert durch API", @@ -184,6 +185,7 @@ "map.loading.service": "", "map.navigation.message": "Bitte verwenden Sie STRG + Maus (oder zwei Finger auf einem Mobilgerät), um die Karte zu navigieren", "map.select.layer": "Datenquelle", + "map.wfs.urlInput.hint": "", "map.wms.urlInput.hint": "", "multiselect.filter.placeholder": "Suche", "nav.back": "Zurück", @@ -219,14 +221,16 @@ "record.metadata.catalog": "Katalog", "record.metadata.contact": "Kontakt", "record.metadata.createdOn": "Erstellt am", - "record.metadata.details": "Details", + "record.metadata.details": "Technische Informationen", "record.metadata.download": "Downloads", "record.metadata.formats": "", "record.metadata.isOpenData": "Open Data", "record.metadata.keywords": "Stichworte", + "record.metadata.lastUpdate": "", "record.metadata.links": "Links", "record.metadata.noUsage": "Für diesen Datensatz sind keine Verwendungsbedingungen angegeben.", "record.metadata.origin": "Über die Daten", + "record.metadata.owner": "", "record.metadata.preview": "Vorschau", "record.metadata.publications": "Veröffentlichungen", "record.metadata.quality": "", @@ -249,7 +253,10 @@ "record.metadata.quality.updateFrequency.success": "", "record.metadata.related": "Ähnliche Datensätze", "record.metadata.sheet": "Weitere Informationen erhalten Sie unter :", + "record.metadata.themes": "", "record.metadata.title": "Titel", + "record.metadata.type": "", + "record.metadata.uniqueId": "", "record.metadata.updateFrequency": "Aktualisierungsfrequenz der Daten", "record.metadata.updateStatus": "Aktualisierungsstatus der Daten", "record.metadata.updatedOn": "Letzte Aktualisierung der Dateninformationen", @@ -313,6 +320,7 @@ "table.object.count": "Objekte in diesem Datensatz", "table.select.data": "Datenquelle", "tooltip.html.copy": "HTML kopieren", + "tooltip.id.copy": "", "tooltip.url.copy": "URL kopieren", "tooltip.url.open": "URL öffnen", "ui.readLess": "Weniger lesen", diff --git a/translations/en.json b/translations/en.json index d88988b81b..61c8f11882 100644 --- a/translations/en.json +++ b/translations/en.json @@ -115,7 +115,8 @@ "datahub.news.contact.title": "A specific need?", "datahub.news.feed": "News feed", "datahub.news.figures": "Indicators", - "datahub.search.back": "Back to results", + "datahub.record.addToFavorites": "Add to favorites", + "datahub.search.back": "Back", "datahub.search.filter.all": "All", "datahub.search.filter.generatedByWfs": "generated by an API", "datahub.search.filter.others": "Others", @@ -184,6 +185,7 @@ "map.loading.service": "Loading service...", "map.navigation.message": "Please use CTRL + mouse (or two fingers on mobile) to navigate the map", "map.select.layer": "Data source", + "map.wfs.urlInput.hint": "Enter WFS service URL", "map.wms.urlInput.hint": "Enter WMS service URL", "multiselect.filter.placeholder": "Search", "nav.back": "Back", @@ -219,14 +221,16 @@ "record.metadata.catalog": "Catalog", "record.metadata.contact": "Contact", "record.metadata.createdOn": "Created on", - "record.metadata.details": "Details", + "record.metadata.details": "Technical information", "record.metadata.download": "Downloads", "record.metadata.formats": "Formats", "record.metadata.isOpenData": "Open Data", "record.metadata.keywords": "Keywords", + "record.metadata.lastUpdate": "Last updated on", "record.metadata.links": "Links", "record.metadata.noUsage": "No usage conditions specified for this record.", "record.metadata.origin": "About the data", + "record.metadata.owner": "Catalog of origin", "record.metadata.preview": "Preview", "record.metadata.publications": "publications", "record.metadata.quality": "Metadata Quality", @@ -248,11 +252,14 @@ "record.metadata.quality.updateFrequency.failed": "Update frequency is not completed", "record.metadata.quality.updateFrequency.success": "Update frequency is completed", "record.metadata.related": "Related records", - "record.metadata.sheet": "More information available from:", + "record.metadata.sheet": "Original metadata", + "record.metadata.themes": "Categories", "record.metadata.title": "Title", + "record.metadata.type": "Geographical dataset", + "record.metadata.uniqueId": "Unique Identifier", "record.metadata.updateFrequency": "Data Update Frequency", "record.metadata.updateStatus": "Data Update Status", - "record.metadata.updatedOn": "Last Data Information Update", + "record.metadata.updatedOn": "Metadata's last update", "record.metadata.usage": "Usage & constraints", "record.more.details": "Read more", "record.tab.chart": "Chart", @@ -313,6 +320,7 @@ "table.object.count": "objects in this dataset", "table.select.data": "Data source", "tooltip.html.copy": "Copy HTML", + "tooltip.id.copy": "Copy unique identifier", "tooltip.url.copy": "Copy URL", "tooltip.url.open": "Open URL", "ui.readLess": "Read less", diff --git a/translations/es.json b/translations/es.json index b09de24e42..8923c4b162 100644 --- a/translations/es.json +++ b/translations/es.json @@ -115,6 +115,7 @@ "datahub.news.contact.title": "", "datahub.news.feed": "", "datahub.news.figures": "", + "datahub.record.addToFavorites": "", "datahub.search.back": "", "datahub.search.filter.all": "", "datahub.search.filter.generatedByWfs": "", @@ -184,6 +185,7 @@ "map.loading.service": "", "map.navigation.message": "", "map.select.layer": "", + "map.wfs.urlInput.hint": "", "map.wms.urlInput.hint": "", "multiselect.filter.placeholder": "", "nav.back": "", @@ -224,9 +226,11 @@ "record.metadata.formats": "", "record.metadata.isOpenData": "", "record.metadata.keywords": "", + "record.metadata.lastUpdate": "", "record.metadata.links": "", "record.metadata.noUsage": "", "record.metadata.origin": "", + "record.metadata.owner": "", "record.metadata.preview": "", "record.metadata.publications": "", "record.metadata.quality": "", @@ -249,7 +253,10 @@ "record.metadata.quality.updateFrequency.success": "", "record.metadata.related": "", "record.metadata.sheet": "", + "record.metadata.themes": "", "record.metadata.title": "", + "record.metadata.type": "", + "record.metadata.uniqueId": "", "record.metadata.updateFrequency": "", "record.metadata.updateStatus": "", "record.metadata.updatedOn": "", @@ -313,6 +320,7 @@ "table.object.count": "", "table.select.data": "", "tooltip.html.copy": "", + "tooltip.id.copy": "", "tooltip.url.copy": "", "tooltip.url.open": "", "ui.readLess": "", diff --git a/translations/fr.json b/translations/fr.json index dc12283fb0..dfbbedfd0a 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -115,7 +115,8 @@ "datahub.news.contact.title": "Un besoin spécifique ?", "datahub.news.feed": "Fil d'activité", "datahub.news.figures": "Quelques chiffres", - "datahub.search.back": "Retour aux résultats", + "datahub.record.addToFavorites": "Ajouter aux favoris", + "datahub.search.back": "Retour", "datahub.search.filter.all": "Tous", "datahub.search.filter.generatedByWfs": "généré par une API", "datahub.search.filter.others": "Autres", @@ -184,6 +185,7 @@ "map.loading.service": "", "map.navigation.message": "Veuillez utiliser CTRL + souris (ou deux doigts sur mobile) pour naviguer sur la carte", "map.select.layer": "Source de données", + "map.wfs.urlInput.hint": "", "map.wms.urlInput.hint": "", "multiselect.filter.placeholder": "Rechercher", "nav.back": "Retour", @@ -219,14 +221,16 @@ "record.metadata.catalog": "Catalogue", "record.metadata.contact": "Contact", "record.metadata.createdOn": "Créé le", - "record.metadata.details": "Détails", + "record.metadata.details": "Informations techniques", "record.metadata.download": "Téléchargements", "record.metadata.formats": "Formats", "record.metadata.isOpenData": "Donnée Ouverte", "record.metadata.keywords": "Mots clés", + "record.metadata.lastUpdate": "Mis à jour le", "record.metadata.links": "Liens", "record.metadata.noUsage": "Aucune condition d'utilisation spécifiée pour ces données", "record.metadata.origin": "À propos des données", + "record.metadata.owner": "Catalogue d'origine", "record.metadata.preview": "Aperçu", "record.metadata.publications": "données", "record.metadata.quality": "Qualité des métadonnées", @@ -248,11 +252,14 @@ "record.metadata.quality.updateFrequency.failed": "Fréquence de mise à jour n'est pas renseignée", "record.metadata.quality.updateFrequency.success": "Fréquence de mise à jour est renseignée", "record.metadata.related": "Voir aussi", - "record.metadata.sheet": "Plus d'informations à l'adresse suivante :", + "record.metadata.sheet": "Fiche de métadonnées d'origine", + "record.metadata.themes": "Catégories", "record.metadata.title": "Titre", + "record.metadata.type": "Donnée géographique", + "record.metadata.uniqueId": "Identificateur de ressource unique", "record.metadata.updateFrequency": "Fréquence de mise à jour des données", "record.metadata.updateStatus": "Statut de mise à jour des données", - "record.metadata.updatedOn": "Dernière mise à jour des informations sur les données", + "record.metadata.updatedOn": "Mise à jour de la fiche de métadonnée", "record.metadata.usage": "Conditions d'utilisation", "record.more.details": "Détails", "record.tab.chart": "Graphique", @@ -313,6 +320,7 @@ "table.object.count": "enregistrements dans ces données", "table.select.data": "Source de données", "tooltip.html.copy": "Copier le HTML", + "tooltip.id.copy": "Copier l'identifiant unique", "tooltip.url.copy": "Copier l'URL", "tooltip.url.open": "Ouvrir l'URL", "ui.readLess": "Réduire", diff --git a/translations/it.json b/translations/it.json index ac0dd5912e..7116c0dbce 100644 --- a/translations/it.json +++ b/translations/it.json @@ -110,12 +110,13 @@ "datahub.header.organisations": "Organizzazioni", "datahub.header.popularRecords": "Più popolari", "datahub.header.title.html": "
Tutti i dati
pubblici della mia organizzazione
", - "datahub.news.contact.contactus": "", - "datahub.news.contact.html": "", - "datahub.news.contact.title": "", + "datahub.news.contact.contactus": "Contattateci", + "datahub.news.contact.html": "

Ha bisogno di dati che non sono attualmente disponibili sulla piattaforma?

I nostri team sono qui per aiutarla.

", + "datahub.news.contact.title": "Ha un bisogno specifico?", "datahub.news.feed": "Feed di attività", "datahub.news.figures": "Alcune figure", - "datahub.search.back": "Torna ai risultati", + "datahub.record.addToFavorites": "Aggiungere a miei preferiti", + "datahub.search.back": "Ritorna", "datahub.search.filter.all": "Tutti", "datahub.search.filter.generatedByWfs": "generato da un'API", "datahub.search.filter.others": "Altri", @@ -142,7 +143,7 @@ "downloads.format.unknown": "sconosciuto", "downloads.wfs.featuretype.not.found": "Il layer non è stato trovato", "dropFile": "Trascina il suo file", - "externalviewer.dataset.unnamed": "", + "externalviewer.dataset.unnamed": "Layer del datahub", "facets.block.title.OrgForResource": "Organizzazione", "facets.block.title.availableInServices": "Disponibile per", "facets.block.title.cl_hierarchyLevel.key": "Tipo di risorsa", @@ -184,6 +185,7 @@ "map.loading.service": "", "map.navigation.message": "Si prega di utilizzare CTRL + mouse (o due dita su mobile) per navigare sulla mappa", "map.select.layer": "Sorgente dati", + "map.wfs.urlInput.hint": "", "map.wms.urlInput.hint": "", "multiselect.filter.placeholder": "Cerca", "nav.back": "Indietro", @@ -219,14 +221,16 @@ "record.metadata.catalog": "Catalogo", "record.metadata.contact": "Contatto", "record.metadata.createdOn": "Creato il", - "record.metadata.details": "Dettagli", + "record.metadata.details": "Informazioni tecniche", "record.metadata.download": "Download", "record.metadata.formats": "Formati", "record.metadata.isOpenData": "Data aperta", "record.metadata.keywords": "Parole chiave", + "record.metadata.lastUpdate": "Ultimo aggiornamento", "record.metadata.links": "Collegamenti", "record.metadata.noUsage": "Nessuna condizione d'uso specificata per questi dati", "record.metadata.origin": "Informazioni sui dati", + "record.metadata.owner": "Catalogo originale", "record.metadata.preview": "Anteprima", "record.metadata.publications": "pubblicazioni", "record.metadata.quality": "Qualità dei metadati", @@ -248,11 +252,14 @@ "record.metadata.quality.updateFrequency.failed": "La frequenza di aggiornamento non è specificata", "record.metadata.quality.updateFrequency.success": "La frequenza di aggiornamento è specificata", "record.metadata.related": "Vedi anche", - "record.metadata.sheet": "Per ulteriori informazioni, cliccate qui:", + "record.metadata.sheet": "Origine del metadata", + "record.metadata.themes": "Categorie", "record.metadata.title": "Titolo", + "record.metadata.type": "Dati geografici", + "record.metadata.uniqueId": "Identificatore unico di risorsa", "record.metadata.updateFrequency": "Frequenza di aggiornamento dei dati", "record.metadata.updateStatus": "Stato di aggiornamento dei dati", - "record.metadata.updatedOn": "Ultimo aggiornamento delle informazioni sui dati", + "record.metadata.updatedOn": "Ultimo aggiornamento del metadata", "record.metadata.usage": "Condizioni d'uso", "record.more.details": "Dettagli", "record.tab.chart": "Grafico", @@ -261,10 +268,10 @@ "record.was.created.time": "ha creato questi dati {time}", "records": "record", "results.layout.selectOne": "Mostra risultati", - "results.records.hits.displayedOn": "", + "results.records.hits.displayedOn": "{displayed, plural, =0{Nessun record} one{1 record visualizzato} other{{displayed} records visualizzati}} {hits, plural, other{su {hits} in totale.}}", "results.records.hits.empty.help.html": "Suggerimenti:
  • Prova con altre parole chiave
  • Cerca con meno parole
", "results.records.hits.found": "{hits, plural, =0{Nessuna corrispondenza.} one{1 record trovato.} other{{hits} risultati.}}", - "results.records.hits.selected": "", + "results.records.hits.selected": "{amount, plural, one{1 selezionato} other{{ amount } selezionati}}", "results.showMore": "Altri risultati...", "results.sortBy.dateStamp": "Più recente", "results.sortBy.popularity": "Popolarità", @@ -313,10 +320,11 @@ "table.object.count": "record in questi dati", "table.select.data": "Sorgente dati", "tooltip.html.copy": "Copiare il HTML", + "tooltip.id.copy": "Copiare l'identificatore unico", "tooltip.url.copy": "Copiare l'URL", "tooltip.url.open": "Aprire l'URL", - "ui.readLess": "", - "ui.readMore": "", + "ui.readLess": "Ridurre", + "ui.readMore": "Leggi di più", "wfs.featuretype.notfound": "La classe di oggetto non è stata trovata nel servizio", "wfs.geojsongml.notsupported": "Il servizio non supporta il formato GeoJSON o GML", "wfs.unreachable.cors": "Il servizio non è accessibile a causa di limitazioni CORS", diff --git a/translations/nl.json b/translations/nl.json index ca63b4edf5..61e72a4db8 100644 --- a/translations/nl.json +++ b/translations/nl.json @@ -115,6 +115,7 @@ "datahub.news.contact.title": "", "datahub.news.feed": "", "datahub.news.figures": "", + "datahub.record.addToFavorites": "", "datahub.search.back": "", "datahub.search.filter.all": "", "datahub.search.filter.generatedByWfs": "", @@ -184,6 +185,7 @@ "map.loading.service": "", "map.navigation.message": "", "map.select.layer": "", + "map.wfs.urlInput.hint": "", "map.wms.urlInput.hint": "", "multiselect.filter.placeholder": "", "nav.back": "", @@ -224,9 +226,11 @@ "record.metadata.formats": "", "record.metadata.isOpenData": "", "record.metadata.keywords": "", + "record.metadata.lastUpdate": "", "record.metadata.links": "", "record.metadata.noUsage": "", "record.metadata.origin": "", + "record.metadata.owner": "", "record.metadata.preview": "", "record.metadata.publications": "", "record.metadata.quality": "", @@ -249,7 +253,10 @@ "record.metadata.quality.updateFrequency.success": "", "record.metadata.related": "", "record.metadata.sheet": "", + "record.metadata.themes": "", "record.metadata.title": "", + "record.metadata.type": "", + "record.metadata.uniqueId": "", "record.metadata.updateFrequency": "", "record.metadata.updateStatus": "", "record.metadata.updatedOn": "", @@ -313,6 +320,7 @@ "table.object.count": "", "table.select.data": "", "tooltip.html.copy": "", + "tooltip.id.copy": "", "tooltip.url.copy": "", "tooltip.url.open": "", "ui.readLess": "", diff --git a/translations/pt.json b/translations/pt.json index d6107e81a6..ae7c49754b 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -115,6 +115,7 @@ "datahub.news.contact.title": "", "datahub.news.feed": "", "datahub.news.figures": "", + "datahub.record.addToFavorites": "", "datahub.search.back": "", "datahub.search.filter.all": "", "datahub.search.filter.generatedByWfs": "", @@ -184,6 +185,7 @@ "map.loading.service": "", "map.navigation.message": "", "map.select.layer": "", + "map.wfs.urlInput.hint": "", "map.wms.urlInput.hint": "", "multiselect.filter.placeholder": "", "nav.back": "", @@ -224,9 +226,11 @@ "record.metadata.formats": "", "record.metadata.isOpenData": "", "record.metadata.keywords": "", + "record.metadata.lastUpdate": "", "record.metadata.links": "", "record.metadata.noUsage": "", "record.metadata.origin": "", + "record.metadata.owner": "", "record.metadata.preview": "", "record.metadata.publications": "", "record.metadata.quality": "", @@ -249,7 +253,10 @@ "record.metadata.quality.updateFrequency.success": "", "record.metadata.related": "", "record.metadata.sheet": "", + "record.metadata.themes": "", "record.metadata.title": "", + "record.metadata.type": "", + "record.metadata.uniqueId": "", "record.metadata.updateFrequency": "", "record.metadata.updateStatus": "", "record.metadata.updatedOn": "", @@ -313,6 +320,7 @@ "table.object.count": "", "table.select.data": "", "tooltip.html.copy": "", + "tooltip.id.copy": "", "tooltip.url.copy": "", "tooltip.url.open": "", "ui.readLess": "", diff --git a/translations/sk.json b/translations/sk.json index 9271be5ff3..875937f552 100644 --- a/translations/sk.json +++ b/translations/sk.json @@ -115,6 +115,7 @@ "datahub.news.contact.title": "", "datahub.news.feed": "Spravodajský kanál", "datahub.news.figures": "Ukazovatele", + "datahub.record.addToFavorites": "", "datahub.search.back": "Späť na výsledky", "datahub.search.filter.all": "Všetky", "datahub.search.filter.generatedByWfs": "vygenerované cez API", @@ -184,6 +185,7 @@ "map.loading.service": "", "map.navigation.message": "Použite prosím CTRL + myš (alebo dva prsty na mobilnom zariadení) na navigáciu po mape", "map.select.layer": "Zdroj dát", + "map.wfs.urlInput.hint": "", "map.wms.urlInput.hint": "", "multiselect.filter.placeholder": "Hľadať", "nav.back": "Späť", @@ -224,9 +226,11 @@ "record.metadata.formats": "Formáty", "record.metadata.isOpenData": "Otvorené dáta", "record.metadata.keywords": "Kľúčové slová", + "record.metadata.lastUpdate": "", "record.metadata.links": "Odkazy", "record.metadata.noUsage": "Pre túto položku nie sú špecifikované žiadne podmienky použitia.", "record.metadata.origin": "Pôvod", + "record.metadata.owner": "", "record.metadata.preview": "Náhľad", "record.metadata.publications": "publikácie", "record.metadata.quality": "Kvalita metadát", @@ -249,7 +253,10 @@ "record.metadata.quality.updateFrequency.success": "", "record.metadata.related": "Súvisiace záznamy", "record.metadata.sheet": "Ďalšie metadáta sú k dispozícii na:", + "record.metadata.themes": "", "record.metadata.title": "Názov", + "record.metadata.type": "", + "record.metadata.uniqueId": "", "record.metadata.updateFrequency": "Frekvencia aktualizácie metadát", "record.metadata.updateStatus": "Stav aktualizácie metadát", "record.metadata.updatedOn": "Posledná aktualizácia metadát", @@ -313,6 +320,7 @@ "table.object.count": "", "table.select.data": "", "tooltip.html.copy": "", + "tooltip.id.copy": "", "tooltip.url.copy": "", "tooltip.url.open": "", "ui.readLess": "",