Merge pull request #3892 from atmire/w2p-119612_export-item-limit

UI warning for export item limit
This commit is contained in:
Tim Donohue
2025-04-29 12:42:48 -05:00
committed by GitHub
5 changed files with 68 additions and 6 deletions

View File

@@ -1,9 +1,22 @@
<ng-template #tipContent>
<div class="tooltip-content">
<p class="m-0">{{tooltipMsg | translate}}</p>
</div>
</ng-template>
<ng-template #tipContentWarning>
<div class="tooltip-content">
<p class="m-0">{{tooltipMsg | translate}}</p>
<p class="m-0 text-warning">{{exportLimitExceededMsg}}</p>
</div>
</ng-template>
@if (shouldShowButton$ | async) {
<button
class="export-button btn btn-dark btn-sm"
[ngbTooltip]="tooltipMsg | translate"
[ngbTooltip]="(shouldShowWarning$ | async) ? tipContentWarning : tipContent"
(click)="export()"
[title]="tooltipMsg |translate" [attr.aria-label]="tooltipMsg |translate">
[title]="tooltipMsg | translate" [attr.aria-label]="(shouldShowWarning$ | async) ? ((tooltipMsg | translate) + ' ' + exportLimitExceededMsg): (tooltipMsg | translate)">
<i class="fas fa-file-export fa-fw"></i>
</button>
}
}

View File

@@ -9,6 +9,7 @@ import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule } from '@ngx-translate/core';
import { of as observableOf } from 'rxjs';
import { ConfigurationDataService } from '../../../core/data/configuration-data.service';
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
import { ScriptDataService } from '../../../core/data/processes/script-data.service';
import { getProcessDetailRoute } from '../../../process-page/process-page-routing.paths';
@@ -31,6 +32,7 @@ describe('SearchExportCsvComponent', () => {
let authorizationDataService: AuthorizationDataService;
let notificationsService;
let router;
let configurationDataService: jasmine.SpyObj<ConfigurationDataService>;
const process = Object.assign(new Process(), { processId: 5, scriptName: 'metadata-export-search' });
@@ -45,6 +47,10 @@ describe('SearchExportCsvComponent', () => {
],
});
configurationDataService = jasmine.createSpyObj('ConfigurationDataService', {
findByPropertyName: observableOf({ payload: { value: '500' } }),
});
function initBeforeEachAsync() {
scriptDataService = jasmine.createSpyObj('scriptDataService', {
scriptWithNameExistsAndCanExecute: observableOf(true),
@@ -64,6 +70,7 @@ describe('SearchExportCsvComponent', () => {
{ provide: AuthorizationDataService, useValue: authorizationDataService },
{ provide: NotificationsService, useValue: notificationsService },
{ provide: Router, useValue: router },
{ provide: ConfigurationDataService, useValue: configurationDataService },
],
}).compileComponents();
}

View File

@@ -2,7 +2,9 @@ import { AsyncPipe } from '@angular/common';
import {
Component,
Input,
OnChanges,
OnInit,
SimpleChanges,
} from '@angular/core';
import { Router } from '@angular/router';
import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
@@ -18,10 +20,12 @@ import {
switchMap,
} from 'rxjs/operators';
import { ConfigurationDataService } from '../../../core/data/configuration-data.service';
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
import { ScriptDataService } from '../../../core/data/processes/script-data.service';
import { RemoteData } from '../../../core/data/remote-data';
import { ConfigurationProperty } from '../../../core/shared/configuration-property.model';
import { getFirstCompletedRemoteData } from '../../../core/shared/operators';
import { getProcessDetailRoute } from '../../../process-page/process-page-routing.paths';
import { Process } from '../../../process-page/processes/process.model';
@@ -43,13 +47,18 @@ import { SearchFilter } from '../models/search-filter.model';
/**
* Display a button to export the current search results as csv
*/
export class SearchExportCsvComponent implements OnInit {
export class SearchExportCsvComponent implements OnInit, OnChanges {
/**
* The current configuration of the search
*/
@Input() searchConfig: PaginatedSearchOptions;
/**
* The total number of items in the search results which can be exported
*/
@Input() total: number;
/**
* Observable used to determine whether the button should be shown
*/
@@ -60,12 +69,18 @@ export class SearchExportCsvComponent implements OnInit {
*/
tooltipMsg = 'metadata-export-search.tooltip';
exportLimitExceededKey = 'metadata-export-search.submit.error.limit-exceeded';
exportLimitExceededMsg = '';
shouldShowWarning$: Observable<boolean>;
constructor(private scriptDataService: ScriptDataService,
private authorizationDataService: AuthorizationDataService,
private notificationsService: NotificationsService,
private translateService: TranslateService,
private router: Router,
) {
private configurationService: ConfigurationDataService) {
}
ngOnInit(): void {
@@ -75,6 +90,31 @@ export class SearchExportCsvComponent implements OnInit {
map((canExecute: boolean) => canExecute),
startWith(false),
);
this.shouldShowWarning$ = this.itemExceeds();
}
ngOnChanges(changes: SimpleChanges): void {
if (changes.total) {
this.shouldShowWarning$ = this.itemExceeds();
}
}
/**
* Checks if the export limit has been exceeded and updates the tooltip accordingly
*/
private itemExceeds(): Observable<boolean> {
return this.configurationService.findByPropertyName('bulkedit.export.max.items').pipe(
getFirstCompletedRemoteData(),
map((response: RemoteData<ConfigurationProperty>) => {
const limit = Number(response.payload?.values?.[0]) || 500;
if (limit < this.total) {
this.exportLimitExceededMsg = this.translateService.instant(this.exportLimitExceededKey, { limit: String(limit) });
return true;
} else {
return false;
}
}),
);
}
/**

View File

@@ -15,7 +15,7 @@
<h1>{{ (configuration ? configuration + '.search.results.head' : 'search.results.head') | translate }}</h1>
}
@if (showCsvExport) {
<ds-search-export-csv [searchConfig]="searchConfig"></ds-search-export-csv>
<ds-search-export-csv [searchConfig]="searchConfig" [total]="searchResults?.payload?.totalElements"></ds-search-export-csv>
}
</div>
@if (searchResults && searchResults?.hasSucceeded && !searchResults?.isLoading && searchResults?.payload?.page.length > 0) {

View File

@@ -7050,4 +7050,6 @@
"metadata-export-filtered-items.columns.warning": "CSV export automatically includes all relevant fields, so selections in this list are not taken into account.",
"embargo.listelement.badge": "Embargo until {{ date }}",
"metadata-export-search.submit.error.limit-exceeded": "Only the first {{limit}} items will be exported",
}