diff --git a/apps/datahub-e2e/src/e2e/datasetDetailPage.cy.ts b/apps/datahub-e2e/src/e2e/datasetDetailPage.cy.ts
index a13e935c01..26d431145b 100644
--- a/apps/datahub-e2e/src/e2e/datasetDetailPage.cy.ts
+++ b/apps/datahub-e2e/src/e2e/datasetDetailPage.cy.ts
@@ -508,3 +508,122 @@ describe('record with file distributions', () => {
.should('deep.eq', ['csv (csv)', 'json (json)', 'geojson (geojson)'])
})
})
+
+describe('api cards', () => {
+ beforeEach(() => {
+ cy.visit('/dataset/accroche_velos')
+ cy.get('gn-ui-api-card').first().as('firstCard')
+ })
+
+ it('should display the open panel button', () => {
+ cy.get('@firstCard')
+ .find('button')
+ .children('mat-icon')
+ .should('have.text', 'more_horiz')
+ })
+ it('should open and close the panel on click on open panel button', () => {
+ cy.get('@firstCard').click()
+ cy.get('gn-ui-record-api-form').should('be.visible')
+ cy.get('@firstCard').click()
+ cy.get('gn-ui-record-api-form').should('not.be.visible')
+ })
+})
+
+describe('api form', () => {
+ beforeEach(() => {
+ cy.visit('/dataset/accroche_velos')
+ cy.get('gn-ui-api-card').first().find('button').click()
+ cy.get('gn-ui-record-api-form').children('div').as('apiForm')
+ })
+ it('should have request inputs', () => {
+ cy.get('@apiForm').find('gn-ui-text-input').should('have.length', 2)
+ cy.get('@apiForm').find('gn-ui-dropdown-selector').should('have.length', 1)
+ cy.get('@apiForm')
+ .children('div')
+ .first()
+ .children('div')
+ .first()
+ .find('button')
+ .should('have.length', 1)
+ cy.get('@apiForm').find('gn-ui-copy-text-button').should('have.length', 1)
+ })
+ it('should change url on input change', () => {
+ cy.get('@apiForm')
+ .find('gn-ui-copy-text-button')
+ .find('input')
+ .invoke('val')
+ .then((url) => {
+ cy.get('@apiForm').find('gn-ui-text-input').first().clear()
+ cy.get('@apiForm').find('gn-ui-text-input').first().type('54')
+ cy.get('@apiForm')
+ .find('gn-ui-copy-text-button')
+ .find('input')
+ .invoke('val')
+ .then((newUrl) => {
+ expect(newUrl).to.not.eq(url)
+ expect(newUrl).to.include('54')
+ })
+ })
+ })
+ it('should set limit to zero on click on "All" button', () => {
+ cy.get('@apiForm').find('gn-ui-text-input').first().clear()
+ cy.get('@apiForm').find('gn-ui-text-input').first().type('54')
+ cy.get('@apiForm').find('input[type="checkbox"]').check()
+ cy.get('@apiForm').find('gn-ui-text-input').first().should('have.value', '')
+ })
+ it('should reset all 3 inputs and link on click', () => {
+ cy.get('@apiForm').find('gn-ui-text-input').first().as('firstInput')
+ cy.get('@firstInput').clear()
+ cy.get('@firstInput').type('54')
+
+ cy.get('@apiForm').find('gn-ui-text-input').eq(1).as('secondInput')
+ cy.get('@secondInput').clear()
+ cy.get('@secondInput').type('87')
+
+ cy.get('@apiForm').find('gn-ui-dropdown-selector').click()
+ cy.get('button[data-cy-value="csv"]').click()
+
+ cy.get('@apiForm')
+ .find('gn-ui-copy-text-button')
+ .find('input')
+ .invoke('val')
+ .should('include', 'offset=87&limit=54&f=csv')
+
+ cy.get('@apiForm').children('div').first().find('button').first().click()
+
+ cy.get('@firstInput').find('input').should('have.value', '')
+ cy.get('@secondInput').find('input').should('have.value', '')
+ cy.get('@apiForm')
+ .find('gn-ui-dropdown-selector')
+ .find('button')
+ .children('div')
+ .should('have.text', ' JSON ')
+ cy.get('@apiForm')
+ .find('gn-ui-copy-text-button')
+ .find('input')
+ .invoke('val')
+ .should('include', 'f=json')
+ })
+ it('should close the panel on click', () => {
+ cy.get('gn-ui-record-api-form').prev().find('button').click()
+ cy.get('gn-ui-record-api-form').should('not.be.visible')
+ })
+ it('should switch to other card url if card already open', () => {
+ cy.get('@apiForm')
+ .find('gn-ui-copy-text-button')
+ .find('input')
+ .invoke('val')
+ .then((url) => {
+ cy.get('@apiForm').find('gn-ui-text-input').first().clear()
+ cy.get('@apiForm').find('gn-ui-text-input').first().type('54')
+ cy.get('gn-ui-api-card').eq(1).find('button').click()
+ cy.get('@apiForm')
+ .find('gn-ui-copy-text-button')
+ .find('input')
+ .invoke('val')
+ .then((newUrl) => {
+ expect(newUrl).to.not.eq(url)
+ })
+ })
+ })
+})
diff --git a/apps/datahub-e2e/src/e2e/home.cy.ts b/apps/datahub-e2e/src/e2e/home.cy.ts
index 46883d3de6..339b81e756 100644
--- a/apps/datahub-e2e/src/e2e/home.cy.ts
+++ b/apps/datahub-e2e/src/e2e/home.cy.ts
@@ -1,7 +1,7 @@
/* eslint-disable cypress/no-unnecessary-waiting */
import 'cypress-real-events'
-describe('header', () => {
+describe('home', () => {
beforeEach(() => cy.visit('/'))
describe('general display', () => {
diff --git a/apps/datahub/src/app/record/record-apis/record-apis.component.css b/apps/datahub/src/app/record/record-apis/record-apis.component.css
index e69de29bb2..5b19499cd1 100644
--- a/apps/datahub/src/app/record/record-apis/record-apis.component.css
+++ b/apps/datahub/src/app/record/record-apis/record-apis.component.css
@@ -0,0 +1,3 @@
+.tab-header-label {
+ @apply uppercase text-sm text-primary opacity-75 hover:text-primary-darker;
+}
diff --git a/apps/datahub/src/app/record/record-apis/record-apis.component.html b/apps/datahub/src/app/record/record-apis/record-apis.component.html
index b7b623d5e7..fc5b7d0556 100644
--- a/apps/datahub/src/app/record/record-apis/record-apis.component.html
+++ b/apps/datahub/src/app/record/record-apis/record-apis.component.html
@@ -1,25 +1,66 @@
-
- record.metadata.api
-
-
-
+
-
-
+ record.metadata.api
+
+
+
+
+
+
+
+
+
+
+
+ record.metadata.api.form.title
+
+
+
+
+
+
+
diff --git a/apps/datahub/src/app/record/record-apis/record-apis.component.spec.ts b/apps/datahub/src/app/record/record-apis/record-apis.component.spec.ts
index 948b6b2fc0..28c7318b45 100644
--- a/apps/datahub/src/app/record/record-apis/record-apis.component.spec.ts
+++ b/apps/datahub/src/app/record/record-apis/record-apis.component.spec.ts
@@ -1,19 +1,28 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
-import { Subject } from 'rxjs'
import { RecordApisComponent } from './record-apis.component'
+import { TranslateModule } from '@ngx-translate/core'
+import { DatasetServiceDistribution } from '@geonetwork-ui/common/domain/model/record'
import { MdViewFacade } from '@geonetwork-ui/feature/record'
+import { BehaviorSubject } from 'rxjs'
class MdViewFacadeMock {
- apiLinks$ = new Subject()
+ selectedApiLink$ = new BehaviorSubject([])
}
-describe('DataApisComponent', () => {
+const serviceDistributionMock = {
+ type: 'service',
+ url: new URL('http://myogcapifeatures.test'),
+ accessServiceProtocol: 'ogcFeatures',
+} as DatasetServiceDistribution
+
+describe('RecordApisComponent', () => {
let component: RecordApisComponent
let fixture: ComponentFixture
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [RecordApisComponent],
+ imports: [TranslateModule.forRoot()],
providers: [
{
provide: MdViewFacade,
@@ -21,9 +30,7 @@ describe('DataApisComponent', () => {
},
],
}).compileComponents()
- })
- beforeEach(() => {
fixture = TestBed.createComponent(RecordApisComponent)
component = fixture.componentInstance
fixture.detectChanges()
@@ -32,4 +39,32 @@ describe('DataApisComponent', () => {
it('should create', () => {
expect(component).toBeTruthy()
})
+
+ describe('#openRecordApiForm', () => {
+ beforeEach(() => {
+ component.openRecordApiForm(serviceDistributionMock)
+ })
+ it('should update selectedApiLink', () => {
+ expect(component.selectedApiLink).toEqual(serviceDistributionMock)
+ })
+ it('should update maxHeight for transition', () => {
+ expect(component.maxHeight).toEqual('500px')
+ })
+ it('should update opacity for transition', () => {
+ expect(component.opacity).toEqual(1)
+ })
+ })
+
+ describe('#closeRecordApiForm', () => {
+ it('should update selectedApiLink', () => {
+ component.closeRecordApiForm()
+ expect(component.selectedApiLink).toBeUndefined()
+ })
+ it('should update maxHeight for transition', () => {
+ expect(component.maxHeight).toEqual('0px')
+ })
+ it('should update opacity for transition', () => {
+ expect(component.opacity).toEqual(0)
+ })
+ })
})
diff --git a/apps/datahub/src/app/record/record-apis/record-apis.component.ts b/apps/datahub/src/app/record/record-apis/record-apis.component.ts
index 8bcfd1ce86..1473720d76 100644
--- a/apps/datahub/src/app/record/record-apis/record-apis.component.ts
+++ b/apps/datahub/src/app/record/record-apis/record-apis.component.ts
@@ -1,4 +1,5 @@
-import { Component, ChangeDetectionStrategy } from '@angular/core'
+import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core'
+import { DatasetServiceDistribution } from '@geonetwork-ui/common/domain/model/record'
import { MdViewFacade } from '@geonetwork-ui/feature/record'
@Component({
@@ -7,6 +8,29 @@ import { MdViewFacade } from '@geonetwork-ui/feature/record'
styleUrls: ['./record-apis.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class RecordApisComponent {
+export class RecordApisComponent implements OnInit {
+ maxHeight = '0px'
+ opacity = 0
+ selectedApiLink: DatasetServiceDistribution
constructor(public facade: MdViewFacade) {}
+
+ ngOnInit(): void {
+ this.setStyle(undefined)
+ this.selectedApiLink = undefined
+ }
+
+ openRecordApiForm(link: DatasetServiceDistribution) {
+ this.selectedApiLink = link
+ this.setStyle(link)
+ }
+
+ closeRecordApiForm() {
+ this.selectedApiLink = undefined
+ this.setStyle(undefined)
+ }
+
+ setStyle(link: DatasetServiceDistribution) {
+ this.maxHeight = link === undefined ? '0px' : '500px'
+ this.opacity = link === undefined ? 0 : 1
+ }
}
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 f7f020a96c..e332f92203 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
@@ -114,13 +114,16 @@
-
-
+
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 f1ba243c25..4e449bfc01 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
@@ -1,4 +1,10 @@
-import { Component, NO_ERRORS_SCHEMA } from '@angular/core'
+import {
+ Component,
+ EventEmitter,
+ Input,
+ NO_ERRORS_SCHEMA,
+ Output,
+} from '@angular/core'
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { By } from '@angular/platform-browser'
import { SourcesService } from '@geonetwork-ui/feature/catalog'
@@ -6,11 +12,7 @@ import { MapManagerService } from '@geonetwork-ui/feature/map'
import { SearchService } from '@geonetwork-ui/feature/search'
import {
ErrorType,
- MetadataCatalogComponent,
- MetadataContactComponent,
- MetadataInfoComponent,
SearchResultsErrorComponent,
- UiElementsModule,
} from '@geonetwork-ui/ui/elements'
import { TranslateModule } from '@ngx-translate/core'
import { BehaviorSubject, of } from 'rxjs'
@@ -18,6 +20,13 @@ import { RecordMetadataComponent } from './record-metadata.component'
import { OrganizationsServiceInterface } from '@geonetwork-ui/common/domain/organizations.service.interface'
import { DATASET_RECORDS } from '@geonetwork-ui/common/fixtures'
import { MdViewFacade } from '@geonetwork-ui/feature/record'
+import {
+ CatalogRecord,
+ DatasetRecord,
+ DatasetServiceDistribution,
+ Individual,
+ Organization,
+} from '@geonetwork-ui/common/domain/model/record'
const SAMPLE_RECORD = {
...DATASET_RECORDS[0],
@@ -100,6 +109,46 @@ export class MockDataApisComponent {}
})
export class MockRelatedComponent {}
+@Component({
+ // eslint-disable-next-line @angular-eslint/component-selector
+ selector: 'gn-ui-metadata-info',
+ template: '
',
+})
+export class MockMetadataInfoComponent {
+ @Input() metadata: Partial
+ @Input() incomplete: boolean
+ @Output() keyword = new EventEmitter()
+}
+
+@Component({
+ // eslint-disable-next-line @angular-eslint/component-selector
+ selector: 'gn-ui-metadata-contact',
+ template: '',
+})
+export class MockMetadataContactComponent {
+ @Input() metadata: Partial
+ @Output() organizationClick = new EventEmitter()
+ @Output() contactClick = new EventEmitter()
+}
+
+@Component({
+ // eslint-disable-next-line @angular-eslint/component-selector
+ selector: 'gn-ui-metadata-catalog',
+ template: '',
+})
+export class MockMetadataCatalogComponent {
+ @Input() sourceLabel: string
+}
+
+@Component({
+ // eslint-disable-next-line @angular-eslint/component-selector
+ selector: 'gn-ui-record-api-form',
+ template: '',
+})
+export class MockRecordApiFormComponent {
+ @Input() apiLink: DatasetServiceDistribution
+}
+
describe('RecordMetadataComponent', () => {
let component: RecordMetadataComponent
let fixture: ComponentFixture
@@ -119,9 +168,13 @@ describe('RecordMetadataComponent', () => {
MockDataApisComponent,
MockRelatedComponent,
SearchResultsErrorComponent,
+ MockMetadataInfoComponent,
+ MockMetadataCatalogComponent,
+ MockMetadataContactComponent,
+ MockRecordApiFormComponent,
],
schemas: [NO_ERRORS_SCHEMA],
- imports: [UiElementsModule, TranslateModule.forRoot()],
+ imports: [TranslateModule.forRoot()],
providers: [
{
provide: MdViewFacade,
@@ -161,21 +214,21 @@ describe('RecordMetadataComponent', () => {
})
describe('about', () => {
- let metadataInfo: MetadataInfoComponent
- let metadataContact: MetadataContactComponent
- let catalogComponent: MetadataCatalogComponent
+ let metadataInfo: MockMetadataInfoComponent
+ let metadataContact: MockMetadataContactComponent
+ let catalogComponent: MockMetadataCatalogComponent
beforeEach(() => {
facade.isPresent$.next(true)
fixture.detectChanges()
metadataInfo = fixture.debugElement.query(
- By.directive(MetadataInfoComponent)
+ By.directive(MockMetadataInfoComponent)
).componentInstance
metadataContact = fixture.debugElement.query(
- By.directive(MetadataContactComponent)
+ By.directive(MockMetadataContactComponent)
).componentInstance
catalogComponent = fixture.debugElement.query(
- By.directive(MetadataCatalogComponent)
+ By.directive(MockMetadataCatalogComponent)
).componentInstance
})
describe('if metadata present', () => {
@@ -197,7 +250,7 @@ describe('RecordMetadataComponent', () => {
facade.isPresent$.next(false)
fixture.detectChanges()
metadataInfo = fixture.debugElement.query(
- By.directive(MetadataInfoComponent)
+ By.directive(MockMetadataInfoComponent)
).componentInstance
})
it('shows a placeholder', () => {
@@ -206,12 +259,12 @@ describe('RecordMetadataComponent', () => {
})
it('does not display the metadata contact component', () => {
expect(
- fixture.debugElement.query(By.directive(MetadataContactComponent))
+ fixture.debugElement.query(By.directive(MockMetadataContactComponent))
).toBeFalsy()
})
it('does not display the metadata catalog component', () => {
expect(
- fixture.debugElement.query(By.directive(MetadataCatalogComponent))
+ fixture.debugElement.query(By.directive(MockMetadataCatalogComponent))
).toBeFalsy()
})
})
diff --git a/libs/api/metadata-converter/src/lib/common/distribution.mapper.ts b/libs/api/metadata-converter/src/lib/common/distribution.mapper.ts
index bde80a250e..e5d66e3b66 100644
--- a/libs/api/metadata-converter/src/lib/common/distribution.mapper.ts
+++ b/libs/api/metadata-converter/src/lib/common/distribution.mapper.ts
@@ -5,6 +5,7 @@ export function matchProtocol(protocol: string): ServiceProtocol {
if (/wfs/i.test(protocol)) return 'wfs'
if (/wmts/i.test(protocol)) return 'wmts'
if (/wps/i.test(protocol)) return 'wps'
+ if (/ogc\W*api\W*features/i.test(protocol)) return 'ogcFeatures'
if (/esri/i.test(protocol)) return 'esriRest'
return 'other'
}
diff --git a/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.spec.ts b/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.spec.ts
index 3263349036..ee7619dc54 100644
--- a/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.spec.ts
+++ b/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.spec.ts
@@ -72,6 +72,7 @@ describe('Gn4FieldMapper', () => {
'link',
'link',
'download',
+ 'service',
])
})
})
diff --git a/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts b/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts
index c9ab874094..cda6afbc21 100644
--- a/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts
+++ b/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts
@@ -323,7 +323,8 @@ export class Gn4FieldMapper {
(/^ESRI:REST/.test(protocol) && /FeatureServer/.test(url)) ||
/^OGC:WMS/.test(protocol) ||
/^OGC:WFS/.test(protocol) ||
- /^OGC:WMTS/.test(protocol)
+ /^OGC:WMTS/.test(protocol) ||
+ /ogc\W*api\W*features/i.test(protocol)
) {
return 'service'
}
diff --git a/libs/common/domain/src/lib/model/record/metadata.model.ts b/libs/common/domain/src/lib/model/record/metadata.model.ts
index 90d2fba553..38ebcd6e1e 100644
--- a/libs/common/domain/src/lib/model/record/metadata.model.ts
+++ b/libs/common/domain/src/lib/model/record/metadata.model.ts
@@ -98,6 +98,7 @@ export type ServiceProtocol =
| 'wps'
| 'wmts'
| 'esriRest'
+ | 'ogcFeatures'
| 'other'
export type DatasetDistributionType = 'service' | 'download' | 'link'
diff --git a/libs/common/fixtures/src/lib/elasticsearch/metadata-links.fixtures.ts b/libs/common/fixtures/src/lib/elasticsearch/metadata-links.fixtures.ts
index d09abce176..38b6a80cc2 100644
--- a/libs/common/fixtures/src/lib/elasticsearch/metadata-links.fixtures.ts
+++ b/libs/common/fixtures/src/lib/elasticsearch/metadata-links.fixtures.ts
@@ -141,4 +141,9 @@ export const ES_LINK_FIXTURES: Record = deepFreeze({
name: 'Vue HTML des métadonnées sur internet',
url: 'http://catalogue.geo-ide.developpement-durable.gouv.fr/catalogue/srv/fre/catalog.search#/metadata/fr-120066022-jdd-199fd14c-2abb-4c14-b0f8-6c8d92e7b480',
},
+ geodataogcfeatures: {
+ protocol: 'OGC API - Features',
+ name: 'ogcapi features layer',
+ url: 'https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/comptages_velo/items?',
+ },
})
diff --git a/libs/common/fixtures/src/lib/link.fixtures.ts b/libs/common/fixtures/src/lib/link.fixtures.ts
index 7d1aa5014b..9c25448e77 100644
--- a/libs/common/fixtures/src/lib/link.fixtures.ts
+++ b/libs/common/fixtures/src/lib/link.fixtures.ts
@@ -151,4 +151,11 @@ export const LINK_FIXTURES: Record = deepFreeze({
'http://catalogue.geo-ide.developpement-durable.gouv.fr/catalogue/srv/fre/catalog.search#/metadata/fr-120066022-jdd-199fd14c-2abb-4c14-b0f8-6c8d92e7b480'
),
},
+ ogcApiFormat: {
+ name: 'ogc api features layer',
+ type: 'service',
+ url: new URL(
+ 'https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/comptages_velo/items?'
+ ),
+ },
})
diff --git a/libs/ui/elements/src/lib/api-card/api-card.component.html b/libs/ui/elements/src/lib/api-card/api-card.component.html
index ba834b4d0d..676de845c2 100644
--- a/libs/ui/elements/src/lib/api-card/api-card.component.html
+++ b/libs/ui/elements/src/lib/api-card/api-card.component.html
@@ -1,23 +1,51 @@
{{ link.name || link.description }}
{{ link.accessServiceProtocol.toUpperCase() }}{{ link.accessServiceProtocol }}
+
diff --git a/libs/ui/elements/src/lib/api-card/api-card.component.spec.ts b/libs/ui/elements/src/lib/api-card/api-card.component.spec.ts
index 1261e98035..2da1df059b 100644
--- a/libs/ui/elements/src/lib/api-card/api-card.component.spec.ts
+++ b/libs/ui/elements/src/lib/api-card/api-card.component.spec.ts
@@ -1,18 +1,16 @@
-import { NO_ERRORS_SCHEMA } from '@angular/core'
import { ComponentFixture, TestBed } from '@angular/core/testing'
-import { MatIconModule } from '@angular/material/icon'
-import { TranslateModule } from '@ngx-translate/core'
import { ApiCardComponent } from './api-card.component'
+import { TranslateModule } from '@ngx-translate/core'
+import { DatasetServiceDistribution } from '@geonetwork-ui/common/domain/model/record'
describe('ApiCardComponent', () => {
let component: ApiCardComponent
let fixture: ComponentFixture
-
+ let openRecordApiFormEmit
beforeEach(async () => {
await TestBed.configureTestingModule({
- schemas: [NO_ERRORS_SCHEMA],
declarations: [ApiCardComponent],
- imports: [MatIconModule, TranslateModule.forRoot()],
+ imports: [TranslateModule.forRoot()],
}).compileComponents()
})
@@ -20,16 +18,27 @@ describe('ApiCardComponent', () => {
fixture = TestBed.createComponent(ApiCardComponent)
component = fixture.componentInstance
component.link = {
- name: 'Allroads',
- description: 'A file that contains all roads',
- url: new URL('https://roads.com/wfs'),
- type: 'service',
- accessServiceProtocol: 'wfs',
- }
+ accessServiceProtocol: 'ogcFeatures',
+ } as DatasetServiceDistribution
+ openRecordApiFormEmit = component.openRecordApiForm
+ jest.resetAllMocks()
+ jest.spyOn(openRecordApiFormEmit, 'emit')
fixture.detectChanges()
})
it('should create', () => {
expect(component).toBeTruthy()
})
+
+ it('should initialize custom property based on accessServiceProtocol', () => {
+ component.ngOnInit()
+ expect(component.displayApiFormButton).toBe(true)
+ })
+
+ it('should toggle currentlyActive and emit openRecordApiForm event', () => {
+ component.openRecordApiFormPanel()
+
+ expect(component.currentlyActive).toBe(true)
+ expect(openRecordApiFormEmit.emit).toHaveBeenCalled()
+ })
})
diff --git a/libs/ui/elements/src/lib/api-card/api-card.component.ts b/libs/ui/elements/src/lib/api-card/api-card.component.ts
index 9f9888d3a0..3b47fc48a5 100644
--- a/libs/ui/elements/src/lib/api-card/api-card.component.ts
+++ b/libs/ui/elements/src/lib/api-card/api-card.component.ts
@@ -1,5 +1,14 @@
-import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
import { DatasetServiceDistribution } from '@geonetwork-ui/common/domain/model/record'
+import {
+ ChangeDetectionStrategy,
+ Component,
+ EventEmitter,
+ Input,
+ OnChanges,
+ OnInit,
+ Output,
+ SimpleChanges,
+} from '@angular/core'
@Component({
selector: 'gn-ui-api-card',
@@ -7,6 +16,28 @@ import { DatasetServiceDistribution } from '@geonetwork-ui/common/domain/model/r
styleUrls: ['./api-card.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class ApiCardComponent {
+export class ApiCardComponent implements OnInit, OnChanges {
@Input() link: DatasetServiceDistribution
+ @Input() currentLink: DatasetServiceDistribution
+ displayApiFormButton: boolean
+ currentlyActive = false
+ @Output() openRecordApiForm: EventEmitter =
+ new EventEmitter()
+
+ ngOnInit() {
+ this.displayApiFormButton =
+ this.link.accessServiceProtocol === 'ogcFeatures' ? true : false
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ this.currentlyActive =
+ changes.currentLink.currentValue === this.link ? true : false
+ }
+
+ openRecordApiFormPanel() {
+ if (this.displayApiFormButton) {
+ this.currentlyActive = !this.currentlyActive
+ this.openRecordApiForm.emit(this.currentlyActive ? this.link : undefined)
+ }
+ }
}
diff --git a/libs/ui/elements/src/lib/record-api-form/record-api-form.component.css b/libs/ui/elements/src/lib/record-api-form/record-api-form.component.css
new file mode 100644
index 0000000000..0aa4ba7d0b
--- /dev/null
+++ b/libs/ui/elements/src/lib/record-api-form/record-api-form.component.css
@@ -0,0 +1,22 @@
+:host ::ng-deep input {
+ color: black;
+ opacity: 1;
+}
+
+:host ::ng-deep gn-ui-copy-text-button input[type='text'] {
+ color: black;
+ background-color: white;
+}
+
+:host ::ng-deep gn-ui-copy-text-button button,
+host ::ng-deep gn-ui-copy-text-button button:hover {
+ background-color: var(--color-secondary) !important;
+}
+
+:host ::ng-deep gn-ui-copy-text-button button mat-icon {
+ color: white !important;
+ opacity: 1 !important;
+}
+:host ::ng-deep gn-ui-copy-text-button button:hover mat-icon {
+ color: lightgrey !important;
+}
diff --git a/libs/ui/elements/src/lib/record-api-form/record-api-form.component.html b/libs/ui/elements/src/lib/record-api-form/record-api-form.component.html
new file mode 100644
index 0000000000..14b899fef5
--- /dev/null
+++ b/libs/ui/elements/src/lib/record-api-form/record-api-form.component.html
@@ -0,0 +1,75 @@
+
+
+
+
+ record.metadata.api.form.create
+
+
+
+
+
+
record.metadata.api.form.limit
+
+
+
+
+
+ record.metadata.api.form.limit.all
+
+
+
+
+
record.metadata.api.form.offset
+
+
+
+
+
record.metadata.api.form.type
+
+
+
+
+
+
+ record.metadata.api.form.customUrl
+
+
+
+
+
+
diff --git a/libs/ui/elements/src/lib/record-api-form/record-api-form.component.spec.ts b/libs/ui/elements/src/lib/record-api-form/record-api-form.component.spec.ts
new file mode 100644
index 0000000000..d21e700497
--- /dev/null
+++ b/libs/ui/elements/src/lib/record-api-form/record-api-form.component.spec.ts
@@ -0,0 +1,90 @@
+import { TestBed, ComponentFixture } from '@angular/core/testing'
+import { RecordApiFormComponent } from './record-api-form.component'
+import { DatasetServiceDistribution } from '@geonetwork-ui/common/domain/model/record'
+import { firstValueFrom } from 'rxjs'
+import { UiInputsModule } from '@geonetwork-ui/ui/inputs'
+import { TranslateModule } from '@ngx-translate/core'
+
+const mockDatasetServiceDistribution: DatasetServiceDistribution = {
+ url: new URL('https://api.example.com/data'),
+ type: 'service',
+ accessServiceProtocol: 'ogcFeatures',
+}
+
+describe('RecordApFormComponent', () => {
+ let component: RecordApiFormComponent
+ let fixture: ComponentFixture
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [RecordApiFormComponent],
+ imports: [UiInputsModule, TranslateModule.forRoot()],
+ }).compileComponents()
+
+ fixture = TestBed.createComponent(RecordApiFormComponent)
+ component = fixture.componentInstance
+ component.apiLink = mockDatasetServiceDistribution
+ fixture.detectChanges()
+ })
+
+ it('should create', () => {
+ expect(component).toBeTruthy()
+ })
+ describe('When panel is opened', () => {
+ it('should set the links and initial values correctly', async () => {
+ expect(component.apiBaseUrl).toBe('https://api.example.com/data')
+ expect(component.offset$.getValue()).toBe('')
+ expect(component.limit$.getValue()).toBe('')
+ expect(component.format$.getValue()).toBe('json')
+ const url = await firstValueFrom(component.apiQueryUrl$)
+ expect(url).toBe('https://api.example.com/data?f=json')
+ })
+ })
+ describe('When URL params are changed', () => {
+ it('should update query URL correctly when setting offset, limit, and format', async () => {
+ const mockOffset = '10'
+ const mockLimit = '20'
+ const mockFormat = 'json'
+ component.setOffset(mockOffset)
+ component.setLimit(mockLimit)
+ component.setFormat(mockFormat)
+ const url = await firstValueFrom(component.apiQueryUrl$)
+ expect(url).toBe(
+ `https://api.example.com/data?offset=${mockOffset}&limit=${mockLimit}&f=${mockFormat}`
+ )
+ })
+ it('should remove the param in url if value is null', async () => {
+ const mockOffset = null
+ const mockLimit = '20'
+ const mockFormat = 'json'
+ component.setOffset(mockOffset)
+ component.setLimit(mockLimit)
+ component.setFormat(mockFormat)
+ const url = await firstValueFrom(component.apiQueryUrl$)
+ expect(url).toBe(
+ `https://api.example.com/data?limit=${mockLimit}&f=${mockFormat}`
+ )
+ })
+ it('should remove the param in url if value is zero', async () => {
+ const mockOffset = '10'
+ const mockLimit = '0'
+ const mockFormat = 'json'
+ component.setOffset(mockOffset)
+ component.setLimit(mockLimit)
+ component.setFormat(mockFormat)
+ const url = await firstValueFrom(component.apiQueryUrl$)
+ expect(url).toBe(
+ `https://api.example.com/data?offset=${mockOffset}&f=${mockFormat}`
+ )
+ })
+ })
+
+ describe('#resetUrl', () => {
+ it('should reset URL to default parameters', () => {
+ component.resetUrl()
+ expect(component.offset$.getValue()).toBe('')
+ expect(component.limit$.getValue()).toBe('')
+ expect(component.format$.getValue()).toBe('json')
+ })
+ })
+})
diff --git a/libs/ui/elements/src/lib/record-api-form/record-api-form.component.ts b/libs/ui/elements/src/lib/record-api-form/record-api-form.component.ts
new file mode 100644
index 0000000000..497eddab1a
--- /dev/null
+++ b/libs/ui/elements/src/lib/record-api-form/record-api-form.component.ts
@@ -0,0 +1,65 @@
+import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
+import { DatasetServiceDistribution } from '@geonetwork-ui/common/domain/model/record'
+import { BehaviorSubject, combineLatest, map } from 'rxjs'
+
+const DEFAULT_PARAMS = {
+ OFFSET: '',
+ LIMIT: '',
+ FORMAT: 'json',
+}
+@Component({
+ selector: 'gn-ui-record-api-form',
+ templateUrl: './record-api-form.component.html',
+ styleUrls: ['./record-api-form.component.css'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class RecordApiFormComponent {
+ @Input() set apiLink(value: DatasetServiceDistribution) {
+ this.apiBaseUrl = value ? value.url.href : undefined
+ this.resetUrl()
+ }
+ offset$ = new BehaviorSubject('')
+ limit$ = new BehaviorSubject('')
+ format$ = new BehaviorSubject('')
+ apiBaseUrl: string
+ formatsList = [
+ { label: 'JSON', value: 'json' },
+ { label: 'CSV', value: 'csv' },
+ ]
+ apiQueryUrl$ = combineLatest([this.offset$, this.limit$, this.format$]).pipe(
+ map(([offset, limit, format]) => {
+ let outputUrl
+ if (this.apiBaseUrl) {
+ const url = new URL(this.apiBaseUrl)
+ const params = { offset: offset, limit: limit, f: format }
+ for (const [key, value] of Object.entries(params)) {
+ if (value && value !== '0') {
+ url.searchParams.set(key, value)
+ } else {
+ url.searchParams.delete(key)
+ }
+ }
+ outputUrl = url.toString()
+ }
+ return outputUrl
+ })
+ )
+
+ setOffset(value: string) {
+ this.offset$.next(value)
+ }
+
+ setLimit(value: string) {
+ this.limit$.next(value)
+ }
+
+ setFormat(value: string) {
+ this.format$.next(value)
+ }
+
+ resetUrl() {
+ this.offset$.next(DEFAULT_PARAMS.OFFSET)
+ this.limit$.next(DEFAULT_PARAMS.LIMIT)
+ this.format$.next(DEFAULT_PARAMS.FORMAT)
+ }
+}
diff --git a/libs/ui/elements/src/lib/ui-elements.module.ts b/libs/ui/elements/src/lib/ui-elements.module.ts
index 377675c95f..73b00327a2 100644
--- a/libs/ui/elements/src/lib/ui-elements.module.ts
+++ b/libs/ui/elements/src/lib/ui-elements.module.ts
@@ -28,6 +28,7 @@ import { UserPreviewComponent } from './user-preview/user-preview.component'
import { GnUiLinkifyDirective } from './metadata-info/linkify.directive'
import { PaginationButtonsComponent } from './pagination-buttons/pagination-buttons.component'
import { MaxLinesComponent } from './max-lines/max-lines.component'
+import { RecordApiFormComponent } from './record-api-form/record-api-form.component'
@NgModule({
imports: [
@@ -63,6 +64,7 @@ import { MaxLinesComponent } from './max-lines/max-lines.component'
GnUiLinkifyDirective,
PaginationButtonsComponent,
MaxLinesComponent,
+ RecordApiFormComponent,
],
exports: [
MetadataInfoComponent,
@@ -82,6 +84,7 @@ import { MaxLinesComponent } from './max-lines/max-lines.component'
AvatarComponent,
UserPreviewComponent,
PaginationButtonsComponent,
+ RecordApiFormComponent,
],
})
export class UiElementsModule {}
diff --git a/libs/ui/inputs/src/lib/date-range-picker/date-range-picker.component.css b/libs/ui/inputs/src/lib/date-range-picker/date-range-picker.component.css
new file mode 100644
index 0000000000..58dae5804f
--- /dev/null
+++ b/libs/ui/inputs/src/lib/date-range-picker/date-range-picker.component.css
@@ -0,0 +1,3 @@
+mat-datepicker-toggle {
+ @apply text-primary;
+}
diff --git a/libs/ui/inputs/src/lib/date-range-picker/date-range-picker.component.html b/libs/ui/inputs/src/lib/date-range-picker/date-range-picker.component.html
new file mode 100644
index 0000000000..dc7eb44a9e
--- /dev/null
+++ b/libs/ui/inputs/src/lib/date-range-picker/date-range-picker.component.html
@@ -0,0 +1,24 @@
+
diff --git a/libs/ui/inputs/src/lib/date-range-picker/date-range-picker.component.spec.ts b/libs/ui/inputs/src/lib/date-range-picker/date-range-picker.component.spec.ts
new file mode 100644
index 0000000000..d3fe405cef
--- /dev/null
+++ b/libs/ui/inputs/src/lib/date-range-picker/date-range-picker.component.spec.ts
@@ -0,0 +1,38 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing'
+import { MatDatepickerInputEvent } from '@angular/material/datepicker'
+import { DateRangePickerComponent } from './date-range-picker.component'
+
+describe('DateRangePickerComponent', () => {
+ let component: DateRangePickerComponent
+ let fixture: ComponentFixture
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [DateRangePickerComponent],
+ }).compileComponents()
+
+ fixture = TestBed.createComponent(DateRangePickerComponent)
+ component = fixture.componentInstance
+ fixture.detectChanges()
+ })
+
+ it('should create', () => {
+ expect(component).toBeTruthy()
+ })
+
+ it('should set start date on startDateSelected', () => {
+ const event = {
+ value: new Date('2023-01-01'),
+ } as MatDatepickerInputEvent
+ component.startDateSelected(event)
+ expect(component.startDate).toEqual(new Date('2023-01-01'))
+ })
+
+ it('should set end date on endDateSelected', () => {
+ const event = {
+ value: new Date('2023-01-31'),
+ } as MatDatepickerInputEvent
+ component.endDateSelected(event)
+ expect(component.endDate).toEqual(new Date('2023-01-31'))
+ })
+})
diff --git a/libs/ui/inputs/src/lib/date-range-picker/date-range-picker.component.ts b/libs/ui/inputs/src/lib/date-range-picker/date-range-picker.component.ts
new file mode 100644
index 0000000000..a4593cdf75
--- /dev/null
+++ b/libs/ui/inputs/src/lib/date-range-picker/date-range-picker.component.ts
@@ -0,0 +1,20 @@
+import { Component } from '@angular/core'
+import { MatDatepickerInputEvent } from '@angular/material/datepicker'
+
+@Component({
+ selector: 'gn-ui-date-range-picker',
+ templateUrl: './date-range-picker.component.html',
+ styleUrls: ['./date-range-picker.component.css'],
+})
+export class DateRangePickerComponent {
+ startDate: Date
+ endDate: Date
+
+ startDateSelected(event: MatDatepickerInputEvent) {
+ this.startDate = event.value
+ }
+
+ endDateSelected(event: MatDatepickerInputEvent) {
+ this.endDate = event.value
+ }
+}
diff --git a/libs/ui/inputs/src/lib/ui-inputs.module.ts b/libs/ui/inputs/src/lib/ui-inputs.module.ts
index 4e9fa30fe7..9960dde5e5 100644
--- a/libs/ui/inputs/src/lib/ui-inputs.module.ts
+++ b/libs/ui/inputs/src/lib/ui-inputs.module.ts
@@ -36,6 +36,11 @@ import { MatTooltipModule } from '@angular/material/tooltip'
import { CommonModule } from '@angular/common'
import { CheckboxComponent } from './checkbox/checkbox.component'
import { SearchInputComponent } from './search-input/search-input.component'
+import { DateRangePickerComponent } from './date-range-picker/date-range-picker.component'
+import { MatFormFieldModule } from '@angular/material/form-field'
+import { MatInputModule } from '@angular/material/input'
+import { MatDatepickerModule } from '@angular/material/datepicker'
+import { MatNativeDateModule } from '@angular/material/core'
@NgModule({
declarations: [
@@ -62,6 +67,7 @@ import { SearchInputComponent } from './search-input/search-input.component'
CopyTextButtonComponent,
CheckboxComponent,
SearchInputComponent,
+ DateRangePickerComponent,
],
imports: [
CommonModule,
@@ -77,6 +83,10 @@ import { SearchInputComponent } from './search-input/search-input.component'
OverlayModule,
MatCheckboxModule,
MatTooltipModule,
+ MatFormFieldModule,
+ MatInputModule,
+ MatDatepickerModule,
+ MatNativeDateModule,
],
exports: [
DropdownSelectorComponent,
@@ -95,6 +105,7 @@ import { SearchInputComponent } from './search-input/search-input.component'
CopyTextButtonComponent,
CheckboxComponent,
SearchInputComponent,
+ DateRangePickerComponent,
],
})
export class UiInputsModule {}
diff --git a/libs/util/shared/src/lib/links/link-classifier.service.ts b/libs/util/shared/src/lib/links/link-classifier.service.ts
index f25fc65103..a900e0400e 100644
--- a/libs/util/shared/src/lib/links/link-classifier.service.ts
+++ b/libs/util/shared/src/lib/links/link-classifier.service.ts
@@ -25,6 +25,8 @@ export class LinkClassifierService {
case 'wms':
case 'wmts':
return [LinkUsage.API, LinkUsage.MAP_API]
+ case 'ogcFeatures':
+ return [LinkUsage.API]
default:
return [LinkUsage.UNKNOWN]
}
diff --git a/support-services/docker-entrypoint-initdb.d/dump b/support-services/docker-entrypoint-initdb.d/dump
index 6a86fefc3d..82c0a6847d 100644
Binary files a/support-services/docker-entrypoint-initdb.d/dump and b/support-services/docker-entrypoint-initdb.d/dump differ
diff --git a/tailwind.base.css b/tailwind.base.css
index 65f9da65b2..c3d617cc36 100644
--- a/tailwind.base.css
+++ b/tailwind.base.css
@@ -16,7 +16,7 @@
}
.card-icon {
- @apply text-primary opacity-25 group-hover:text-secondary group-hover:opacity-100 transition-colors transition-opacity;
+ @apply text-primary opacity-50 group-hover:text-secondary group-hover:opacity-100 transition-colors transition-opacity;
}
}
diff --git a/translations/de.json b/translations/de.json
index 1e0a26dc11..c7851b1cd3 100644
--- a/translations/de.json
+++ b/translations/de.json
@@ -195,6 +195,17 @@
"record.externalViewer.open": "In externem Kartenviewer öffnen",
"record.metadata.about": "Beschreibung",
"record.metadata.api": "API",
+ "record.metadata.api.form.closeButton": "",
+ "record.metadata.api.form.closeForm": "",
+ "record.metadata.api.form.create": "",
+ "record.metadata.api.form.customUrl": "",
+ "record.metadata.api.form.limit": "",
+ "record.metadata.api.form.limit.all": "",
+ "record.metadata.api.form.offset": "",
+ "record.metadata.api.form.openForm": "",
+ "record.metadata.api.form.reset": "",
+ "record.metadata.api.form.title": "",
+ "record.metadata.api.form.type": "",
"record.metadata.author": "",
"record.metadata.catalog": "Katalog",
"record.metadata.contact": "Kontakt",
diff --git a/translations/en.json b/translations/en.json
index 1f4136473c..4c67003036 100644
--- a/translations/en.json
+++ b/translations/en.json
@@ -195,6 +195,17 @@
"record.externalViewer.open": "Open in the external map viewer",
"record.metadata.about": "Description",
"record.metadata.api": "API",
+ "record.metadata.api.form.closeButton": "Close",
+ "record.metadata.api.form.closeForm": "Close the form",
+ "record.metadata.api.form.create": "Create your request",
+ "record.metadata.api.form.customUrl": "Custom URL",
+ "record.metadata.api.form.limit": "Count of records",
+ "record.metadata.api.form.limit.all": "All",
+ "record.metadata.api.form.offset": "Count of first record",
+ "record.metadata.api.form.openForm": "Open the form",
+ "record.metadata.api.form.reset": "Reset",
+ "record.metadata.api.form.title": "Generate a custom URL",
+ "record.metadata.api.form.type": "Output format",
"record.metadata.author": "Author",
"record.metadata.catalog": "Catalog",
"record.metadata.contact": "Contact",
diff --git a/translations/es.json b/translations/es.json
index 6449d111b3..9c939e7902 100644
--- a/translations/es.json
+++ b/translations/es.json
@@ -195,6 +195,17 @@
"record.externalViewer.open": "",
"record.metadata.about": "",
"record.metadata.api": "",
+ "record.metadata.api.form.closeButton": "",
+ "record.metadata.api.form.closeForm": "",
+ "record.metadata.api.form.create": "",
+ "record.metadata.api.form.customUrl": "",
+ "record.metadata.api.form.limit": "",
+ "record.metadata.api.form.limit.all": "",
+ "record.metadata.api.form.offset": "",
+ "record.metadata.api.form.openForm": "",
+ "record.metadata.api.form.reset": "",
+ "record.metadata.api.form.title": "",
+ "record.metadata.api.form.type": "",
"record.metadata.author": "",
"record.metadata.catalog": "",
"record.metadata.contact": "",
diff --git a/translations/fr.json b/translations/fr.json
index 86b7c8bb8b..276facad04 100644
--- a/translations/fr.json
+++ b/translations/fr.json
@@ -195,6 +195,17 @@
"record.externalViewer.open": "Ouvrir dans le visualiseur externe",
"record.metadata.about": "Description",
"record.metadata.api": "API",
+ "record.metadata.api.form.closeButton": "Fermer",
+ "record.metadata.api.form.closeForm": "Fermer le panneau de personnalisation",
+ "record.metadata.api.form.create": "Paramétrer votre requête",
+ "record.metadata.api.form.customUrl": "URL personnalisée",
+ "record.metadata.api.form.limit": "Nombre d'enregistrements",
+ "record.metadata.api.form.limit.all": "Tous",
+ "record.metadata.api.form.offset": "Numéro du 1er enregistrement",
+ "record.metadata.api.form.openForm": "Ouvrir le panneau de personnalisation",
+ "record.metadata.api.form.reset": "Réinitialiser",
+ "record.metadata.api.form.title": "Générer une URL personnalisée",
+ "record.metadata.api.form.type": "Format en sortie",
"record.metadata.author": "Auteur",
"record.metadata.catalog": "Catalogue",
"record.metadata.contact": "Contact",
diff --git a/translations/it.json b/translations/it.json
index 39aadac908..4637132568 100644
--- a/translations/it.json
+++ b/translations/it.json
@@ -195,6 +195,17 @@
"record.externalViewer.open": "",
"record.metadata.about": "",
"record.metadata.api": "",
+ "record.metadata.api.form.closeButton": "",
+ "record.metadata.api.form.closeForm": "",
+ "record.metadata.api.form.create": "",
+ "record.metadata.api.form.customUrl": "",
+ "record.metadata.api.form.limit": "",
+ "record.metadata.api.form.limit.all": "",
+ "record.metadata.api.form.offset": "",
+ "record.metadata.api.form.openForm": "",
+ "record.metadata.api.form.reset": "",
+ "record.metadata.api.form.title": "",
+ "record.metadata.api.form.type": "",
"record.metadata.author": "",
"record.metadata.catalog": "",
"record.metadata.contact": "",
diff --git a/translations/nl.json b/translations/nl.json
index 0a042f1f4e..d9facff482 100644
--- a/translations/nl.json
+++ b/translations/nl.json
@@ -195,6 +195,17 @@
"record.externalViewer.open": "",
"record.metadata.about": "",
"record.metadata.api": "",
+ "record.metadata.api.form.closeButton": "",
+ "record.metadata.api.form.closeForm": "",
+ "record.metadata.api.form.create": "",
+ "record.metadata.api.form.customUrl": "",
+ "record.metadata.api.form.limit": "",
+ "record.metadata.api.form.limit.all": "",
+ "record.metadata.api.form.offset": "",
+ "record.metadata.api.form.openForm": "",
+ "record.metadata.api.form.reset": "",
+ "record.metadata.api.form.title": "",
+ "record.metadata.api.form.type": "",
"record.metadata.author": "",
"record.metadata.catalog": "",
"record.metadata.contact": "",
diff --git a/translations/pt.json b/translations/pt.json
index ad8e77e03d..a801e8cc6c 100644
--- a/translations/pt.json
+++ b/translations/pt.json
@@ -195,6 +195,17 @@
"record.externalViewer.open": "",
"record.metadata.about": "",
"record.metadata.api": "",
+ "record.metadata.api.form.closeButton": "",
+ "record.metadata.api.form.closeForm": "",
+ "record.metadata.api.form.create": "",
+ "record.metadata.api.form.customUrl": "",
+ "record.metadata.api.form.limit": "",
+ "record.metadata.api.form.limit.all": "",
+ "record.metadata.api.form.offset": "",
+ "record.metadata.api.form.openForm": "",
+ "record.metadata.api.form.reset": "",
+ "record.metadata.api.form.title": "",
+ "record.metadata.api.form.type": "",
"record.metadata.author": "",
"record.metadata.catalog": "",
"record.metadata.contact": "",
diff --git a/translations/sk.json b/translations/sk.json
index fc38f91c41..a4b051898d 100644
--- a/translations/sk.json
+++ b/translations/sk.json
@@ -195,6 +195,17 @@
"record.externalViewer.open": "Otvoriť v externom mapovom prehliadači",
"record.metadata.about": "O",
"record.metadata.api": "API",
+ "record.metadata.api.form.closeButton": "",
+ "record.metadata.api.form.closeForm": "",
+ "record.metadata.api.form.create": "",
+ "record.metadata.api.form.customUrl": "",
+ "record.metadata.api.form.limit": "",
+ "record.metadata.api.form.limit.all": "",
+ "record.metadata.api.form.offset": "",
+ "record.metadata.api.form.openForm": "",
+ "record.metadata.api.form.reset": "",
+ "record.metadata.api.form.title": "",
+ "record.metadata.api.form.type": "",
"record.metadata.author": "Autor",
"record.metadata.catalog": "Katalóg",
"record.metadata.contact": "Kontakt",