From 28c0b55355bffa1a12ab7f5825c4acf52d77a3a3 Mon Sep 17 00:00:00 2001 From: Romuald Caplier Date: Thu, 20 Jun 2024 14:18:51 +0200 Subject: [PATCH] feature(metadata-editor): added FormFieldOverviews component to the FormFieldComponent. --- .../model/metadataResource.api.model.ts | 2 +- .../overview-upload.component.html | 1 + .../overview-upload.component.ts | 115 +++++++++++++++--- .../form-field-overviews.component.css | 0 .../form-field-overviews.component.html | 5 + .../form-field-overviews.component.spec.ts | 40 ++++++ .../form-field-overviews.component.ts | 22 ++++ .../form-field/form-field.component.html | 6 + .../form-field/form-field.component.spec.ts | 36 +++++- .../form-field/form-field.component.ts | 15 ++- libs/feature/editor/src/lib/fields.config.ts | 12 ++ .../image-input/image-input.component.html | 2 +- .../lib/image-input/image-input.component.ts | 9 +- 13 files changed, 239 insertions(+), 26 deletions(-) create mode 100644 libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.css create mode 100644 libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.html create mode 100644 libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.spec.ts create mode 100644 libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.ts diff --git a/libs/data-access/gn4/src/openapi/model/metadataResource.api.model.ts b/libs/data-access/gn4/src/openapi/model/metadataResource.api.model.ts index b7d69cb4a2..206f8f87d2 100644 --- a/libs/data-access/gn4/src/openapi/model/metadataResource.api.model.ts +++ b/libs/data-access/gn4/src/openapi/model/metadataResource.api.model.ts @@ -19,7 +19,7 @@ export interface MetadataResourceApiModel { metadataResourceExternalManagementProperties?: MetadataResourceExternalManagementPropertiesApiModel lastModification?: string version?: string - url?: string + url?: URL filename?: string id?: string size?: number diff --git a/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.html b/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.html index 54da3e35a7..78ac6cee4f 100644 --- a/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.html +++ b/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.html @@ -2,6 +2,7 @@ [maxSizeMB]="5" [previewUrl]="resourceUrl" [altText]="imageAltText" + [formControl]="formControl" (fileChange)="handleFileChange($event)" (urlChange)="handleUrlChange($event)" (delete)="handleDelete()" diff --git a/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.ts b/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.ts index 66535d5533..ce32eddf0f 100644 --- a/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.ts +++ b/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.ts @@ -2,12 +2,27 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, + EventEmitter, Input, + OnDestroy, OnInit, + Output, } from '@angular/core' import { CommonModule } from '@angular/common' import { RecordsApiService } from '@geonetwork-ui/data-access/gn4' import { UiInputsModule } from '@geonetwork-ui/ui/inputs' +import { FormControl } from '@angular/forms' +import { GraphicOverview } from '@geonetwork-ui/common/domain/model/record' +import { Subject, takeUntil } from 'rxjs' + +const extractFileNameFormUrl = (url: URL, metadataUuid: string): string => { + const pattern = new RegExp( + `records/${metadataUuid}/attachments/([^/?#]+)(?:[/?#]|$)`, + 'i' + ) + const match = url.href.match(pattern) + return match ? match[1] : '' +} @Component({ selector: 'gn-ui-overview-upload', @@ -17,11 +32,15 @@ import { UiInputsModule } from '@geonetwork-ui/ui/inputs' styleUrls: ['./overview-upload.component.css'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class OverviewUploadComponent implements OnInit { +export class OverviewUploadComponent implements OnInit, OnDestroy { @Input() metadataUuid: string + @Input() formControl!: FormControl + @Output() overviewChange = new EventEmitter() imageAltText: string - resourceUrl: string + resourceUrl: URL + + private destroy$ = new Subject() constructor( private recordsApiService: RecordsApiService, @@ -31,40 +50,98 @@ export class OverviewUploadComponent implements OnInit { ngOnInit(): void { this.recordsApiService .getAllResources(this.metadataUuid) - .subscribe((resources) => { - this.imageAltText = resources[0]?.filename - this.resourceUrl = resources[0]?.url - this.cd.markForCheck() + .pipe(takeUntil(this.destroy$)) + .subscribe({ + next: (resources) => { + if (resources && resources.length > 0) { + this.resourceUrl = new URL(resources[0]?.url) + this.imageAltText = resources[0].filename + } else if (this.formControl.value[0]) { + this.resourceUrl = new URL(this.formControl.value[0].url.href) + this.imageAltText = this.formControl.value[0].description + } else { + this.resourceUrl = null + this.imageAltText = '' + } + + this.cd.markForCheck() + }, + error: this.errorHandle, }) } handleFileChange(file: File) { this.recordsApiService .putResource(this.metadataUuid, file, 'public') - .subscribe((resource) => { - this.imageAltText = resource.filename - this.resourceUrl = resource.url - this.cd.markForCheck() + .pipe(takeUntil(this.destroy$)) + .subscribe({ + next: (resource) => { + this.resourceUrl = new URL(resource.url) + this.imageAltText = resource.filename + + this.overviewChange.emit({ + url: new URL(resource.url), + description: resource.filename, + }) + + this.cd.markForCheck() + }, + error: this.errorHandle, }) } handleUrlChange(url: string) { this.recordsApiService .putResourceFromURL(this.metadataUuid, url, 'public') - .subscribe((resource) => { - this.imageAltText = resource.filename - this.resourceUrl = resource.url - this.cd.markForCheck() + .pipe(takeUntil(this.destroy$)) + .subscribe({ + next: (resource) => { + this.resourceUrl = new URL(resource.url) + this.imageAltText = resource.filename + + this.overviewChange.emit({ + url: new URL(resource.url), + description: resource.filename, + }) + + this.cd.markForCheck() + }, + error: this.errorHandle, }) } handleDelete() { + const fileName = extractFileNameFormUrl(this.resourceUrl, this.metadataUuid) + this.recordsApiService - .delResource(this.metadataUuid, this.imageAltText) - .subscribe(() => { - this.imageAltText = null - this.resourceUrl = null - this.cd.markForCheck() + .delResource(this.metadataUuid, fileName) + .pipe(takeUntil(this.destroy$)) + .subscribe({ + next: () => { + this.imageAltText = null + this.resourceUrl = null + + this.overviewChange.emit(null) + + this.cd.markForCheck() + }, + error: this.errorHandle, }) } + + private errorHandle = (error: never) => { + console.error(error) + + this.resourceUrl = null + this.imageAltText = '' + + this.overviewChange.emit(null) + + this.cd.markForCheck() + } + + ngOnDestroy(): void { + this.destroy$.next() + this.destroy$.complete() + } } diff --git a/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.css b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.html b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.html new file mode 100644 index 0000000000..31a67c2683 --- /dev/null +++ b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.html @@ -0,0 +1,5 @@ + diff --git a/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.spec.ts b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.spec.ts new file mode 100644 index 0000000000..95021ce4f5 --- /dev/null +++ b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.spec.ts @@ -0,0 +1,40 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing' + +import { FormFieldOverviewsComponent } from './form-field-overviews.component' +import { HttpClientTestingModule } from '@angular/common/http/testing' +import { TranslateModule } from '@ngx-translate/core' +import { FormControl } from '@angular/forms' + +describe('FormFieldOverviewsComponent', () => { + let component: FormFieldOverviewsComponent + let fixture: ComponentFixture + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + FormFieldOverviewsComponent, + HttpClientTestingModule, + TranslateModule.forRoot(), + ], + }).compileComponents() + + fixture = TestBed.createComponent(FormFieldOverviewsComponent) + component = fixture.componentInstance + component.metadataUuid = '8505d991-e38f-4704-a47a-e7d335dfbef5' + const control = new FormControl() + control.setValue([ + { + description: 'doge.jpg', + url: new URL( + 'http://localhost:8080/geonetwork/srv/api/0.1/records/8505d991-e38f-4704-a47a-e7d335dfbef5/attachments/doge.jpg' + ), + }, + ]) + component.control = control + fixture.detectChanges() + }) + + it('should create', () => { + expect(component).toBeTruthy() + }) +}) diff --git a/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.ts b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.ts new file mode 100644 index 0000000000..88af229862 --- /dev/null +++ b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.ts @@ -0,0 +1,22 @@ +import { CommonModule } from '@angular/common' +import { ChangeDetectionStrategy, Component, Input } from '@angular/core' +import { OverviewUploadComponent } from '../../../overview-upload/overview-upload.component' +import { FormControl } from '@angular/forms' +import { GraphicOverview } from '@geonetwork-ui/common/domain/model/record' + +@Component({ + selector: 'gn-ui-form-field-overviews', + templateUrl: './form-field-overviews.component.html', + styleUrls: ['./form-field-overviews.component.css'], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: true, + imports: [CommonModule, OverviewUploadComponent], +}) +export class FormFieldOverviewsComponent { + @Input() metadataUuid: string + @Input() control!: FormControl + + handleOverViewChange(overView: GraphicOverview | null) { + this.control.setValue(overView ? [overView] : []) + } +} diff --git a/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html b/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html index 5ac61e653d..f6fa1f9ee2 100644 --- a/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +++ b/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html @@ -76,6 +76,12 @@ + + + { let component: FormFieldComponent @@ -17,7 +27,18 @@ describe('FormFieldComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [FormFieldComponent, TranslateModule.forRoot()], + imports: [ + FormFieldComponent, + TranslateModule.forRoot(), + HttpClientTestingModule, + ], + schemas: [NO_ERRORS_SCHEMA], + providers: [ + { + provide: EditorFacade, + useClass: EditorFacadeMock, + }, + ], }).compileComponents() fixture = TestBed.createComponent(FormFieldComponent) @@ -149,4 +170,17 @@ describe('FormFieldComponent', () => { expect(formField).toBeTruthy() }) }) + describe('overviews field', () => { + let formField + beforeEach(() => { + component.model = 'overviews' + fixture.detectChanges() + formField = fixture.debugElement.query( + By.directive(FormFieldOverviewsComponent) + ).componentInstance + }) + it('creates an array form field', () => { + expect(formField).toBeTruthy() + }) + }) }) diff --git a/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts b/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts index 44621437a5..d4b20e4fe9 100644 --- a/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +++ b/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts @@ -28,7 +28,9 @@ import { FormFieldSpatialExtentComponent } from './form-field-spatial-extent/for import { FormFieldUpdateFrequencyComponent } from './form-field-update-frequency/form-field-update-frequency.component' import { CatalogRecordKeys } from '@geonetwork-ui/common/domain/model/record' import { FormFieldKeywordsComponent } from './form-field-keywords/form-field-keywords.component' -import { FormFieldConfig } from '../../../models' +import { FormFieldOverviewsComponent } from './form-field-overviews/form-field-overviews.component' +import { map, take } from 'rxjs/operators' +import { EditorFacade } from '../../../+state/editor.facade' @Component({ selector: 'gn-ui-form-field', @@ -55,6 +57,7 @@ import { FormFieldConfig } from '../../../models' FormFieldArrayComponent, FormFieldKeywordsComponent, TranslateModule, + FormFieldOverviewsComponent, ], }) export class FormFieldComponent { @@ -70,9 +73,14 @@ export class FormFieldComponent { @ViewChild('titleInput') titleInput: ElementRef + metadataUuid$ = this.facade.record$.pipe( + take(1), + map((record) => record.uniqueIdentifier) + ) + formControl = new FormControl() - constructor() { + constructor(private facade: EditorFacade) { this.valueChange = this.formControl.valueChanges } @@ -101,6 +109,9 @@ export class FormFieldComponent { get isSpatialExtentField() { return this.model === 'spatialExtents' } + get isGraphicOverview() { + return this.model === 'overviews' + } get isSimpleField() { return this.model === 'uniqueIdentifier' || this.model === 'recordUpdated' } diff --git a/libs/feature/editor/src/lib/fields.config.ts b/libs/feature/editor/src/lib/fields.config.ts index 8c22644b48..cf72fef64d 100644 --- a/libs/feature/editor/src/lib/fields.config.ts +++ b/libs/feature/editor/src/lib/fields.config.ts @@ -167,6 +167,18 @@ export const DEFAULT_CONFIGURATION: EditorConfig = { { labelKey: marker('editor.record.form.page.description'), sections: [TITLE_SECTION, ABOUT_SECTION, GEOGRAPHICAL_COVERAGE_SECTION], + { + model: 'overviews', + formFieldConfig: { + labelKey: marker('editor.record.form.overviews'), + type: 'list', + }, + }, + { + model: 'keywords', + formFieldConfig: { + labelKey: marker('editor.record.form.keywords'), + type: 'list', }, { labelKey: marker('editor.record.form.page.ressources'), diff --git a/libs/ui/inputs/src/lib/image-input/image-input.component.html b/libs/ui/inputs/src/lib/image-input/image-input.component.html index ceeb82456a..ec41523dd2 100644 --- a/libs/ui/inputs/src/lib/image-input/image-input.component.html +++ b/libs/ui/inputs/src/lib/image-input/image-input.component.html @@ -51,7 +51,7 @@