From 872735702f333caf23e844821f0f16db914c3663 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laure-H=C3=A9l=C3=A8ne=20Bruneton?= Date: Thu, 25 Jul 2024 10:01:14 +0200 Subject: [PATCH 01/13] feat(editor): remove unnecessary export --- libs/api/metadata-converter/src/index.ts | 1 - libs/api/repository/src/lib/gn4/gn4-repository.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/libs/api/metadata-converter/src/index.ts b/libs/api/metadata-converter/src/index.ts index a5e5f3d06e..169d8dfcb6 100644 --- a/libs/api/metadata-converter/src/index.ts +++ b/libs/api/metadata-converter/src/index.ts @@ -1,4 +1,3 @@ -export * from './lib/base.converter' export * from './lib/iso19139' export * from './lib/iso19115-3' export * from './lib/find-converter' diff --git a/libs/api/repository/src/lib/gn4/gn4-repository.ts b/libs/api/repository/src/lib/gn4/gn4-repository.ts index d16e72c14d..e916a4d2b4 100644 --- a/libs/api/repository/src/lib/gn4/gn4-repository.ts +++ b/libs/api/repository/src/lib/gn4/gn4-repository.ts @@ -24,7 +24,6 @@ import { } from '@geonetwork-ui/common/domain/model/search' import { catchError, map, tap } from 'rxjs/operators' import { - BaseConverter, findConverterForDocument, Gn4Converter, Gn4SearchResults, From 69705f15a333bd19dd5f5095261a1fa3d506819e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laure-H=C3=A9l=C3=A8ne=20Bruneton?= Date: Fri, 26 Jul 2024 11:52:59 +0200 Subject: [PATCH 02/13] feat(editor): delete a record and its draft --- .../dashboard-menu.component.ts | 10 ++-- .../records/my-draft/my-draft.component.html | 1 + .../records/my-draft/my-draft.component.ts | 1 + .../repository/src/lib/gn4/gn4-repository.ts | 15 ++++- .../records-repository.interface.ts | 7 +++ .../results-table-container.component.html | 1 + .../results-table-container.component.ts | 43 +++++++++++++- .../search/src/lib/state/search.facade.ts | 6 ++ libs/ui/elements/src/index.ts | 1 + .../confirmation-dialog.component.css | 0 .../confirmation-dialog.component.html | 8 +++ .../confirmation-dialog.component.spec.ts | 24 ++++++++ .../confirmation-dialog.component.stories.ts | 47 +++++++++++++++ .../confirmation-dialog.component.ts | 37 ++++++++++++ .../action-menu/action-menu.component.html | 11 +++- .../action-menu/action-menu.component.ts | 57 ++++++++++++++++++- .../results-table.component.html | 6 +- .../results-table/results-table.component.ts | 6 ++ 18 files changed, 267 insertions(+), 14 deletions(-) create mode 100644 libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.css create mode 100644 libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.html create mode 100644 libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.spec.ts create mode 100644 libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.stories.ts create mode 100644 libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.ts diff --git a/apps/metadata-editor/src/app/dashboard/dashboard-menu/dashboard-menu.component.ts b/apps/metadata-editor/src/app/dashboard/dashboard-menu/dashboard-menu.component.ts index 4845040432..e2bb95762b 100644 --- a/apps/metadata-editor/src/app/dashboard/dashboard-menu/dashboard-menu.component.ts +++ b/apps/metadata-editor/src/app/dashboard/dashboard-menu/dashboard-menu.component.ts @@ -4,7 +4,7 @@ import { MatIconModule } from '@angular/material/icon' import { RouterModule } from '@angular/router' import { TranslateModule } from '@ngx-translate/core' import { RecordsRepositoryInterface } from '@geonetwork-ui/common/domain/repository/records-repository.interface' -import { map } from 'rxjs/operators' +import { map, startWith, switchMap } from 'rxjs/operators' import { BadgeComponent } from '@geonetwork-ui/ui/inputs' @Component({ @@ -22,9 +22,11 @@ import { BadgeComponent } from '@geonetwork-ui/ui/inputs' ], }) export class DashboardMenuComponent { - draftsCount$ = this.recordsRepository - .getAllDrafts() - .pipe(map((drafts) => drafts.length)) + draftsCount$ = this.recordsRepository.draftsChanged$.pipe( + startWith(void 0), + switchMap(() => this.recordsRepository.getAllDrafts()), + map((drafts) => drafts.length) + ) constructor(private recordsRepository: RecordsRepositoryInterface) {} } diff --git a/apps/metadata-editor/src/app/records/my-draft/my-draft.component.html b/apps/metadata-editor/src/app/records/my-draft/my-draft.component.html index de8be72269..f6562d467e 100644 --- a/apps/metadata-editor/src/app/records/my-draft/my-draft.component.html +++ b/apps/metadata-editor/src/app/records/my-draft/my-draft.component.html @@ -11,6 +11,7 @@

diff --git a/apps/metadata-editor/src/app/records/my-draft/my-draft.component.ts b/apps/metadata-editor/src/app/records/my-draft/my-draft.component.ts index cd9d312037..851a91914b 100644 --- a/apps/metadata-editor/src/app/records/my-draft/my-draft.component.ts +++ b/apps/metadata-editor/src/app/records/my-draft/my-draft.component.ts @@ -33,6 +33,7 @@ import { startWith } from 'rxjs' export class MyDraftComponent { records$ = this.recordsRepository.getAllDrafts().pipe(startWith([])) hasDraft = () => true + isDraft = () => true constructor( private router: Router, diff --git a/libs/api/repository/src/lib/gn4/gn4-repository.ts b/libs/api/repository/src/lib/gn4/gn4-repository.ts index e916a4d2b4..1df4921968 100644 --- a/libs/api/repository/src/lib/gn4/gn4-repository.ts +++ b/libs/api/repository/src/lib/gn4/gn4-repository.ts @@ -9,6 +9,7 @@ import { from, Observable, of, + Subject, switchMap, throwError, } from 'rxjs' @@ -34,6 +35,9 @@ import { HttpErrorResponse } from '@angular/common/http' @Injectable() export class Gn4Repository implements RecordsRepositoryInterface { + _draftsChanged = new Subject() + draftsChanged$ = this._draftsChanged.asObservable() + constructor( private gn4SearchApi: SearchApiService, private gn4SearchHelper: ElasticsearchService, @@ -244,6 +248,7 @@ export class Gn4Repository implements RecordsRepositoryInterface { this.getLocalStorageKeyForRecord(record.uniqueIdentifier), xml ) + this._draftsChanged.next() return [record, xml, false] as [CatalogRecord, string, false] }) ) @@ -294,17 +299,22 @@ export class Gn4Repository implements RecordsRepositoryInterface { ) } + deleteRecord(uniqueIdentifier: string): Observable { + return this.gn4RecordsApi.deleteRecord(uniqueIdentifier) + } + saveRecordAsDraft( record: CatalogRecord, referenceRecordSource?: string ): Observable { return this.serializeRecordToXml(record, referenceRecordSource).pipe( - tap((recordXml) => + tap((recordXml) => { window.localStorage.setItem( this.getLocalStorageKeyForRecord(record.uniqueIdentifier), recordXml ) - ) + this._draftsChanged.next() + }) ) } @@ -312,6 +322,7 @@ export class Gn4Repository implements RecordsRepositoryInterface { window.localStorage.removeItem( this.getLocalStorageKeyForRecord(uniqueIdentifier) ) + this._draftsChanged.next() } recordHasDraft(uniqueIdentifier: string): boolean { diff --git a/libs/common/domain/src/lib/repository/records-repository.interface.ts b/libs/common/domain/src/lib/repository/records-repository.interface.ts index 039eac3812..57f2d2f100 100644 --- a/libs/common/domain/src/lib/repository/records-repository.interface.ts +++ b/libs/common/domain/src/lib/repository/records-repository.interface.ts @@ -52,6 +52,12 @@ export abstract class RecordsRepositoryInterface { referenceRecordSource?: string ): Observable + /** + * @param uniqueIdentifier + * @returns Observable Returns when record is deleted + */ + abstract deleteRecord(uniqueIdentifier: string): Observable + /** * @param record * @param referenceRecordSource @@ -67,4 +73,5 @@ export abstract class RecordsRepositoryInterface { /** will return all pending drafts, both published and not published */ abstract getAllDrafts(): Observable + abstract draftsChanged$: Observable } diff --git a/libs/feature/search/src/lib/results-table/results-table-container.component.html b/libs/feature/search/src/lib/results-table/results-table-container.component.html index b0aeb07004..3310cf5e8a 100644 --- a/libs/feature/search/src/lib/results-table/results-table-container.component.html +++ b/libs/feature/search/src/lib/results-table/results-table-container.component.html @@ -5,6 +5,7 @@ [sortOrder]="sortBy$ | async" (recordClick)="handleRecordClick($event)" (duplicateRecord)="handleDuplicateRecord($event)" + (deleteRecord)="handleDeleteRecord($event)" (recordsSelectedChange)="handleRecordsSelectedChange($event[0], $event[1])" (sortByChange)="handleSortByChange($event[0], $event[1])" > diff --git a/libs/feature/search/src/lib/results-table/results-table-container.component.ts b/libs/feature/search/src/lib/results-table/results-table-container.component.ts index 6b1cf8ebce..7e15aed314 100644 --- a/libs/feature/search/src/lib/results-table/results-table-container.component.ts +++ b/libs/feature/search/src/lib/results-table/results-table-container.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Output } from '@angular/core' +import { Component, EventEmitter, OnDestroy, Output } from '@angular/core' import { CatalogRecord } from '@geonetwork-ui/common/domain/model/record' import { SearchFacade } from '../state/search.facade' import { SelectionService } from '@geonetwork-ui/api/repository' @@ -6,6 +6,9 @@ import { SearchService } from '../utils/service/search.service' import { RecordsRepositoryInterface } from '@geonetwork-ui/common/domain/repository/records-repository.interface' import { ResultsTableComponent } from '@geonetwork-ui/ui/search' import { CommonModule } from '@angular/common' +import { Subscription } from 'rxjs' +import { NotificationsService } from '@geonetwork-ui/feature/notifications' +import { TranslateService } from '@ngx-translate/core' @Component({ selector: 'gn-ui-results-table-container', @@ -14,10 +17,12 @@ import { CommonModule } from '@angular/common' standalone: true, imports: [CommonModule, ResultsTableComponent], }) -export class ResultsTableContainerComponent { +export class ResultsTableContainerComponent implements OnDestroy { @Output() recordClick = new EventEmitter() @Output() duplicateRecord = new EventEmitter() + subscription = new Subscription() + records$ = this.searchFacade.results$ selectedRecords$ = this.selectionService.selectedRecordsIdentifiers$ sortBy$ = this.searchFacade.sortBy$ @@ -29,7 +34,9 @@ export class ResultsTableContainerComponent { private searchFacade: SearchFacade, private searchService: SearchService, private selectionService: SelectionService, - private recordsRepository: RecordsRepositoryInterface + private recordsRepository: RecordsRepositoryInterface, + private notificationsService: NotificationsService, + private translateService: TranslateService ) {} handleRecordClick(item: unknown) { @@ -40,6 +47,32 @@ export class ResultsTableContainerComponent { this.duplicateRecord.emit(item as CatalogRecord) } + async handleDeleteRecord(item: unknown) { + const uniqueIdentifier = (item as CatalogRecord).uniqueIdentifier + this.subscription.add( + this.recordsRepository.deleteRecord(uniqueIdentifier).subscribe({ + next: () => { + this.recordsRepository.clearRecordDraft(uniqueIdentifier) + this.searchFacade.requestNewResults() + }, + error: (error) => { + this.notificationsService.showNotification({ + type: 'error', + title: this.translateService.instant( + 'editor.record.deleteError.title' + ), + text: `${this.translateService.instant( + 'editor.record.deleteError.body' + )} ${error}`, + closeMessage: this.translateService.instant( + 'editor.record.deleteError.closeMessage' + ), + }) + }, + }) + ) + } + handleSortByChange(col: string, order: 'asc' | 'desc') { this.searchService.setSortBy([order, col]) } @@ -51,4 +84,8 @@ export class ResultsTableContainerComponent { this.selectionService.selectRecords(records) } } + + ngOnDestroy() { + this.subscription.unsubscribe() + } } diff --git a/libs/feature/search/src/lib/state/search.facade.ts b/libs/feature/search/src/lib/state/search.facade.ts index 1f9388a2b1..44c9c34e1e 100644 --- a/libs/feature/search/src/lib/state/search.facade.ts +++ b/libs/feature/search/src/lib/state/search.facade.ts @@ -8,6 +8,7 @@ import { Paginate, RequestMoreOnAggregation, RequestMoreResults, + RequestNewResults, SetConfigAggregations, SetConfigFilters, SetConfigRequestFields, @@ -151,6 +152,11 @@ export class SearchFacade { return this } + requestNewResults(): SearchFacade { + this.store.dispatch(new RequestNewResults(this.searchId)) + return this + } + requestMoreOnAggregation(key: string, increment: number): SearchFacade { this.store.dispatch( new RequestMoreOnAggregation(key, increment, this.searchId) diff --git a/libs/ui/elements/src/index.ts b/libs/ui/elements/src/index.ts index 578e08b8ed..327b9d5c81 100644 --- a/libs/ui/elements/src/index.ts +++ b/libs/ui/elements/src/index.ts @@ -1,5 +1,6 @@ export * from './lib/api-card/api-card.component' export * from './lib/avatar/avatar.component' +export * from './lib/confirmation-dialog/confirmation-dialog.component' export * from './lib/content-ghost/content-ghost.component' export * from './lib/download-item/download-item.component' export * from './lib/downloads-list/downloads-list.component' diff --git a/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.css b/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.html b/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.html new file mode 100644 index 0000000000..2414c3e5f2 --- /dev/null +++ b/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.html @@ -0,0 +1,8 @@ +

{{ data.title }}

+
{{ data.message }}
+
+ {{ data.cancelText }} + {{ + data.confirmText + }} +
diff --git a/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.spec.ts b/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.spec.ts new file mode 100644 index 0000000000..952d289727 --- /dev/null +++ b/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing' +import { ConfirmationDialogComponent } from './confirmation-dialog.component' + +describe('ConfirmationDialogComponent', () => { + let component: ConfirmationDialogComponent + let fixture: ComponentFixture + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ConfirmationDialogComponent], + }).compileComponents() + }) + + beforeEach(() => { + fixture = TestBed.createComponent(ConfirmationDialogComponent) + component = fixture.componentInstance + fixture.detectChanges() + }) + + it('should create', () => { + fixture.detectChanges() + expect(component).toBeTruthy() + }) +}) diff --git a/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.stories.ts b/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.stories.ts new file mode 100644 index 0000000000..6f4decc4ac --- /dev/null +++ b/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.stories.ts @@ -0,0 +1,47 @@ +import { Component, Input } from '@angular/core' +import { MatDialog } from '@angular/material/dialog' +import { ButtonComponent } from '@geonetwork-ui/ui/inputs' +import { Meta, moduleMetadata, StoryObj } from '@storybook/angular' +import { ConfirmationDialogComponent } from './confirmation-dialog.component' + +@Component({ + selector: 'gn-ui-launcher', + template: ` Open `, +}) +class LaunchDialogComponent { + @Input() title = '' + @Input() message = '' + @Input() confirmText = '' + @Input() cancelText = '' + constructor(private _dialog: MatDialog) {} + + launch(): void { + this._dialog.open(ConfirmationDialogComponent, { + data: { + title: this.title, + message: this.message, + confirmText: this.confirmText, + cancelText: this.cancelText, + }, + }) + } +} + +export default { + title: 'Elements/ConfirmationDialogComponent', + component: LaunchDialogComponent, + decorators: [ + moduleMetadata({ + imports: [ButtonComponent, ConfirmationDialogComponent], + }), + ], +} as Meta + +export const Primary: StoryObj = { + args: { + title: 'Some title', + message: 'Some message to confirm', + confirmText: 'OK', + cancelText: 'KO', + }, +} diff --git a/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.ts b/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.ts new file mode 100644 index 0000000000..8eac932175 --- /dev/null +++ b/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.ts @@ -0,0 +1,37 @@ +import { ChangeDetectionStrategy, Component, Inject } from '@angular/core' +import { + MAT_DIALOG_DATA, + MatDialogModule, + MatDialogRef, +} from '@angular/material/dialog' +import { ButtonComponent } from '@geonetwork-ui/ui/inputs' + +export interface ConfirmationDialogData { + title: string + message: string + confirmText: string + cancelText: string +} + +@Component({ + selector: 'gn-ui-confirmation-dialog', + templateUrl: './confirmation-dialog.component.html', + styleUrls: ['./confirmation-dialog.component.css'], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: true, + imports: [MatDialogModule, ButtonComponent], +}) +export class ConfirmationDialogComponent { + constructor( + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: ConfirmationDialogData + ) {} + + onConfirm() { + this.dialogRef.close(true) + } + + onCancel() { + this.dialogRef.close(false) + } +} diff --git a/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.html b/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.html index f423d03193..416d2d50d6 100644 --- a/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.html +++ b/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.html @@ -1,5 +1,5 @@ + diff --git a/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.ts b/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.ts index 155692316e..a2fe6e57f4 100644 --- a/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.ts +++ b/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.ts @@ -1,22 +1,73 @@ -import { Component, EventEmitter, Output, ViewChild } from '@angular/core' +import { + Component, + EventEmitter, + Input, + Output, + ViewChild, +} from '@angular/core' +import { MatDialog, MatDialogModule } from '@angular/material/dialog' import { MatIconModule } from '@angular/material/icon' import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu' +import { ConfirmationDialogComponent } from '@geonetwork-ui/ui/elements' import { ButtonComponent } from '@geonetwork-ui/ui/inputs' -import { TranslateModule } from '@ngx-translate/core' +import { TranslateModule, TranslateService } from '@ngx-translate/core' @Component({ selector: 'gn-ui-action-menu', templateUrl: './action-menu.component.html', styleUrls: ['./action-menu.component.css'], standalone: true, - imports: [MatIconModule, ButtonComponent, MatMenuModule, TranslateModule], + imports: [ + MatIconModule, + ButtonComponent, + MatMenuModule, + MatDialogModule, + ConfirmationDialogComponent, + TranslateModule, + ], }) export class ActionMenuComponent { + @Input() isDraft: boolean @Output() duplicate = new EventEmitter() + @Output() delete = new EventEmitter() @ViewChild(MatMenuTrigger) trigger: MatMenuTrigger + constructor( + public dialog: MatDialog, + private translateService: TranslateService + ) {} + openMenu() { this.trigger.openMenu() } + + openDeleteConfirmationDialog() { + const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + data: { + title: this.translateService.instant( + 'editor.record.delete.confirmation.title' + ), + message: this.translateService.instant( + 'editor.record.delete.confirmation.message' + ), + confirmText: this.translateService.instant( + 'editor.record.delete.confirmation.confirmText' + ), + cancelText: this.translateService.instant( + 'editor.record.delete.confirmation.cancelText' + ), + }, + restoreFocus: false, + }) + + // Manually restore focus to the menu trigger since the element that + // opens the dialog won't be in the DOM any more when the dialog closes. + dialogRef.afterClosed().subscribe((result) => { + this.trigger.focus() + if (result) { + this.delete.emit() + } + }) + } } diff --git a/libs/ui/search/src/lib/results-table/results-table.component.html b/libs/ui/search/src/lib/results-table/results-table.component.html index c062fd521c..6f59bdab1f 100644 --- a/libs/ui/search/src/lib/results-table/results-table.component.html +++ b/libs/ui/search/src/lib/results-table/results-table.component.html @@ -125,7 +125,11 @@ - + diff --git a/libs/ui/search/src/lib/results-table/results-table.component.ts b/libs/ui/search/src/lib/results-table/results-table.component.ts index 3d7dba9f0a..a5153280cf 100644 --- a/libs/ui/search/src/lib/results-table/results-table.component.ts +++ b/libs/ui/search/src/lib/results-table/results-table.component.ts @@ -48,11 +48,13 @@ export class ResultsTableComponent { @Input() selectedRecordsIdentifiers: string[] = [] @Input() sortOrder: SortByField = null @Input() recordHasDraft: (record: CatalogRecord) => boolean = () => false + @Input() recordIsDraft: (record: CatalogRecord) => boolean = () => false // emits the column (field) as well as the order @Output() sortByChange = new EventEmitter<[string, 'asc' | 'desc']>() @Output() recordClick = new EventEmitter() @Output() duplicateRecord = new EventEmitter() + @Output() deleteRecord = new EventEmitter() @Output() recordsSelectedChange = new EventEmitter< [CatalogRecord[], boolean] >() @@ -103,6 +105,10 @@ export class ResultsTableComponent { this.duplicateRecord.emit(item as CatalogRecord) } + handleDelete(item: unknown) { + this.deleteRecord.emit(item as CatalogRecord) + } + setSortBy(col: string, order: 'asc' | 'desc') { this.sortByChange.emit([col, order]) } From bf30cd406b152321ae5930bcd13a239c9d09b982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laure-H=C3=A9l=C3=A8ne=20Bruneton?= Date: Fri, 2 Aug 2024 09:36:45 +0200 Subject: [PATCH 03/13] feat(editor): delete a temporary draft --- .../records/my-draft/my-draft.component.html | 2 ++ .../records/my-draft/my-draft.component.ts | 32 ++++++++++++------- .../repository/src/lib/gn4/gn4-repository.ts | 4 +++ .../records-repository.interface.ts | 1 + .../action-menu/action-menu.component.html | 2 +- .../action-menu/action-menu.component.ts | 1 + .../results-table.component.html | 1 + .../results-table/results-table.component.ts | 10 ++---- 8 files changed, 33 insertions(+), 20 deletions(-) diff --git a/apps/metadata-editor/src/app/records/my-draft/my-draft.component.html b/apps/metadata-editor/src/app/records/my-draft/my-draft.component.html index f6562d467e..bc95b84bc0 100644 --- a/apps/metadata-editor/src/app/records/my-draft/my-draft.component.html +++ b/apps/metadata-editor/src/app/records/my-draft/my-draft.component.html @@ -12,7 +12,9 @@

[records]="records$ | async" [recordHasDraft]="hasDraft" [recordIsDraft]="isDraft" + [draftIsTemporary]="isTemporary" (recordClick)="editRecord($event)" + (deleteRecord)="deleteDraft($event)" > diff --git a/apps/metadata-editor/src/app/records/my-draft/my-draft.component.ts b/apps/metadata-editor/src/app/records/my-draft/my-draft.component.ts index 851a91914b..8ef05cf0b1 100644 --- a/apps/metadata-editor/src/app/records/my-draft/my-draft.component.ts +++ b/apps/metadata-editor/src/app/records/my-draft/my-draft.component.ts @@ -1,17 +1,17 @@ -import { Component } from '@angular/core' import { CommonModule } from '@angular/common' -import { TranslateModule } from '@ngx-translate/core' -import { RecordsListComponent } from '../records-list.component' -import { ResultsTableContainerComponent } from '@geonetwork-ui/feature/search' -import { ButtonComponent } from '@geonetwork-ui/ui/inputs' +import { Component } from '@angular/core' import { MatIconModule } from '@angular/material/icon' -import { RecordsCountComponent } from '../records-count/records-count.component' -import { UiElementsModule } from '@geonetwork-ui/ui/elements' -import { RecordsRepositoryInterface } from '@geonetwork-ui/common/domain/repository/records-repository.interface' -import { CatalogRecord } from '@geonetwork-ui/common/domain/model/record' import { Router } from '@angular/router' +import { CatalogRecord } from '@geonetwork-ui/common/domain/model/record' +import { RecordsRepositoryInterface } from '@geonetwork-ui/common/domain/repository/records-repository.interface' +import { ResultsTableContainerComponent } from '@geonetwork-ui/feature/search' +import { UiElementsModule } from '@geonetwork-ui/ui/elements' +import { ButtonComponent } from '@geonetwork-ui/ui/inputs' import { ResultsTableComponent } from '@geonetwork-ui/ui/search' -import { startWith } from 'rxjs' +import { TranslateModule } from '@ngx-translate/core' +import { startWith, switchMap } from 'rxjs' +import { RecordsCountComponent } from '../records-count/records-count.component' +import { RecordsListComponent } from '../records-list.component' @Component({ selector: 'md-editor-my-my-draft', @@ -31,9 +31,15 @@ import { startWith } from 'rxjs' ], }) export class MyDraftComponent { - records$ = this.recordsRepository.getAllDrafts().pipe(startWith([])) + records$ = this.recordsRepository.draftsChanged$.pipe( + startWith(void 0), + switchMap(() => this.recordsRepository.getAllDrafts()), + startWith([]) + ) hasDraft = () => true isDraft = () => true + isTemporary = (record: CatalogRecord): boolean => + this.recordsRepository.draftIsTemporary(record.uniqueIdentifier) constructor( private router: Router, @@ -43,4 +49,8 @@ export class MyDraftComponent { editRecord(record: CatalogRecord) { this.router.navigate(['/edit', record.uniqueIdentifier]) } + + deleteDraft(record: CatalogRecord) { + this.recordsRepository.clearRecordDraft(record.uniqueIdentifier) + } } diff --git a/libs/api/repository/src/lib/gn4/gn4-repository.ts b/libs/api/repository/src/lib/gn4/gn4-repository.ts index 1df4921968..a8468763c2 100644 --- a/libs/api/repository/src/lib/gn4/gn4-repository.ts +++ b/libs/api/repository/src/lib/gn4/gn4-repository.ts @@ -333,6 +333,10 @@ export class Gn4Repository implements RecordsRepositoryInterface { ) } + draftIsTemporary(uniqueIdentifier: string): boolean { + return uniqueIdentifier.startsWith('TEMP-ID-') + } + // generated by copilot getAllDrafts(): Observable { const items = { ...window.localStorage } diff --git a/libs/common/domain/src/lib/repository/records-repository.interface.ts b/libs/common/domain/src/lib/repository/records-repository.interface.ts index 57f2d2f100..001453595b 100644 --- a/libs/common/domain/src/lib/repository/records-repository.interface.ts +++ b/libs/common/domain/src/lib/repository/records-repository.interface.ts @@ -70,6 +70,7 @@ export abstract class RecordsRepositoryInterface { abstract clearRecordDraft(uniqueIdentifier: string): void abstract recordHasDraft(uniqueIdentifier: string): boolean + abstract draftIsTemporary(uniqueIdentifier: string): boolean /** will return all pending drafts, both published and not published */ abstract getAllDrafts(): Observable diff --git a/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.html b/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.html index 416d2d50d6..de020e865a 100644 --- a/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.html +++ b/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.html @@ -17,7 +17,7 @@