diff --git a/apps/red-ui/src/app/common/filter/filter.component.ts b/apps/red-ui/src/app/common/filter/filter.component.ts index 266514cb4..bcee24caa 100644 --- a/apps/red-ui/src/app/common/filter/filter.component.ts +++ b/apps/red-ui/src/app/common/filter/filter.component.ts @@ -9,7 +9,7 @@ import { MatMenu, MatMenuTrigger } from '@angular/material/menu'; templateUrl: './filter.component.html', styleUrls: ['./filter.component.scss'] }) -export class FilterComponent implements OnChanges { +export class FilterComponent { @Input() filterTemplate: TemplateRef; @Output() filtersChanged = new EventEmitter(); @Input() filters: FilterModel[] = []; @@ -24,31 +24,6 @@ export class FilterComponent implements OnChanges { constructor(public readonly appStateService: AppStateService, private readonly _changeDetectorRef: ChangeDetectorRef) {} - ngOnChanges(changes: SimpleChanges): void { - if (changes.filters) { - const oldFilters = changes.filters.previousValue; - this._copySettings(oldFilters, this.filters); - if (this.filters) { - this.filters.forEach((filter) => { - handleCheckedValue(filter); - }); - } - } - } - - private _copySettings(oldFilters: FilterModel[], newFilters: FilterModel[]) { - if (oldFilters && newFilters) { - for (const oldFilter of oldFilters) { - const newFilter = newFilters.find((f) => f.key === oldFilter.key); - if (newFilter) { - newFilter.checked = oldFilter.checked; - newFilter.indeterminate = oldFilter.indeterminate; - this._copySettings(oldFilter.filters, newFilter.filters); - } - } - } - } - filterCheckboxClicked($event: any, filter: FilterModel, parent?: FilterModel) { filter.checked = !filter.checked; if (parent) { diff --git a/apps/red-ui/src/app/common/filter/utils/filter-utils.ts b/apps/red-ui/src/app/common/filter/utils/filter-utils.ts index e5cb6a1dd..fcc8ab95f 100644 --- a/apps/red-ui/src/app/common/filter/utils/filter-utils.ts +++ b/apps/red-ui/src/app/common/filter/utils/filter-utils.ts @@ -11,6 +11,29 @@ export const RedactionFilterSorter = { none: 4 }; +export function processFilters(oldFilters: FilterModel[], newFilters: FilterModel[]) { + copySettings(oldFilters, newFilters); + if (newFilters) { + newFilters.forEach((filter) => { + handleCheckedValue(filter); + }); + } + return newFilters; +} + +function copySettings(oldFilters: FilterModel[], newFilters: FilterModel[]) { + if (oldFilters && newFilters) { + for (const oldFilter of oldFilters) { + const newFilter = newFilters.find((f) => f.key === oldFilter.key); + if (newFilter) { + newFilter.checked = oldFilter.checked; + newFilter.indeterminate = oldFilter.indeterminate; + copySettings(oldFilter.filters, newFilter.filters); + } + } + } +} + export function handleCheckedValue(filter: FilterModel) { if (filter.filters && filter.filters.length) { filter.checked = filter.filters.reduce((acc, next) => acc && next.checked, true); diff --git a/apps/red-ui/src/app/common/service/permissions.service.ts b/apps/red-ui/src/app/common/service/permissions.service.ts index c544b548f..4e3e1f221 100644 --- a/apps/red-ui/src/app/common/service/permissions.service.ts +++ b/apps/red-ui/src/app/common/service/permissions.service.ts @@ -53,7 +53,8 @@ export class PermissionsService { if (!fileStatus) { fileStatus = this._appStateService.activeFile; } - return (this.fileRequiresReanalysis(fileStatus) && this.isReviewerOrOwner(fileStatus)) || (fileStatus.isError && fileStatus.isUnassigned); + return true; + // return (this.fileRequiresReanalysis(fileStatus) && this.isReviewerOrOwner(fileStatus)) || (fileStatus.isError && fileStatus.isUnassigned); } isFileReviewer(fileStatus?: FileStatusWrapper) { diff --git a/apps/red-ui/src/app/dialogs/dialog.service.ts b/apps/red-ui/src/app/dialogs/dialog.service.ts index aa3396f31..897926669 100644 --- a/apps/red-ui/src/app/dialogs/dialog.service.ts +++ b/apps/red-ui/src/app/dialogs/dialog.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { FileDetailsDialogComponent } from './file-details-dialog/file-details-dialog.component'; import { MatDialog, MatDialogRef } from '@angular/material/dialog'; import { FileStatus, FileUploadControllerService, ManualRedactionControllerService, Project } from '@redaction/red-ui-http'; -import { ConfirmationDialogComponent } from '../common/confirmation-dialog/confirmation-dialog.component'; +import { ConfirmationDialogComponent, ConfirmationDialogInput } from '../common/confirmation-dialog/confirmation-dialog.component'; import { NotificationService, NotificationType } from '../notification/notification.service'; import { TranslateService } from '@ngx-translate/core'; import { AppStateService } from '../state/app-state.service'; @@ -44,7 +44,13 @@ export class DialogService { public openDeleteFilesDialog($event: MouseEvent, projectId: string, fileIds: string[], cb?: Function): MatDialogRef { $event?.stopPropagation(); - const ref = this._dialog.open(ConfirmationDialogComponent, dialogConfig); + const ref = this._dialog.open(ConfirmationDialogComponent, { + ...dialogConfig, + data: new ConfirmationDialogInput({ + title: 'confirmation-dialog.delete-file.title', + question: 'confirmation-dialog.delete-file.question' + }) + }); ref.afterClosed().subscribe((result) => { if (result) { @@ -118,13 +124,19 @@ export class DialogService { return ref; } - public openEditProjectDialog($event: MouseEvent, project: Project): MatDialogRef { + public openEditProjectDialog($event: MouseEvent, project: Project, cb?: Function): MatDialogRef { $event.stopPropagation(); - return this._dialog.open(AddEditProjectDialogComponent, { + const ref = this._dialog.open(AddEditProjectDialogComponent, { ...dialogConfig, autoFocus: true, data: project }); + ref.afterClosed().subscribe(async (result) => { + if (result) { + if (cb) cb(); + } + }); + return ref; } public openDeleteProjectDialog($event: MouseEvent, project: Project, cb?: Function): MatDialogRef { diff --git a/apps/red-ui/src/app/i18n/language.service.ts b/apps/red-ui/src/app/i18n/language.service.ts index 754811054..eb9ff0469 100644 --- a/apps/red-ui/src/app/i18n/language.service.ts +++ b/apps/red-ui/src/app/i18n/language.service.ts @@ -16,7 +16,8 @@ export class LanguageService { chooseAndSetInitialLanguage() { let defaultLang: string; const localStorageLang = localStorage.getItem('redaction.language'); - const browserLang = this.translateService.getBrowserLang(); + // const browserLang = this.translateService.getBrowserLang(); + const browserLang = 'en'; // Force language to english until translations are ready // @ts-ignore if (this.translateService.getLangs().includes(localStorageLang)) { defaultLang = localStorageLang; diff --git a/apps/red-ui/src/app/screens/base-screen/base-screen.component.scss b/apps/red-ui/src/app/screens/base-screen/base-screen.component.scss index 5c1e56a0c..63b3b59b8 100644 --- a/apps/red-ui/src/app/screens/base-screen/base-screen.component.scss +++ b/apps/red-ui/src/app/screens/base-screen/base-screen.component.scss @@ -7,13 +7,13 @@ .breadcrumb { text-decoration: none; - color: $accent; + color: $grey-7; font-weight: 600; width: fit-content; white-space: nowrap; &:last-child { - color: $primary; + color: $accent; @include line-clamp(1); } diff --git a/apps/red-ui/src/app/screens/file/annotation-actions/annotation-actions.component.html b/apps/red-ui/src/app/screens/file/annotation-actions/annotation-actions.component.html index e76ea790f..077d84c3c 100644 --- a/apps/red-ui/src/app/screens/file/annotation-actions/annotation-actions.component.html +++ b/apps/red-ui/src/app/screens/file/annotation-actions/annotation-actions.component.html @@ -1,17 +1,32 @@
- - - @@ -19,20 +34,21 @@ (click)="openMenu($event)" [class.active]="menuOpen" [matMenuTriggerFor]="menu" + [matTooltip]="'annotation-actions.suggest-remove-annotation' | translate" class="confirm" mat-icon-button *ngIf="requiresSuggestionRemoveMenu" > - +
-
+
-
+
diff --git a/apps/red-ui/src/app/screens/file/annotation-actions/annotation-actions.component.ts b/apps/red-ui/src/app/screens/file/annotation-actions/annotation-actions.component.ts index 860371406..ac369f5a4 100644 --- a/apps/red-ui/src/app/screens/file/annotation-actions/annotation-actions.component.ts +++ b/apps/red-ui/src/app/screens/file/annotation-actions/annotation-actions.component.ts @@ -106,4 +106,8 @@ export class AnnotationActionsComponent implements OnInit { get dictionaryColor() { return this.appStateService.getDictionaryColor('suggestion-add-dictionary'); } + + get suggestRemoveLabel() { + return 'suggestRemoveLabel'; + } } diff --git a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.html b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.html index 80e821b64..76b3afcb0 100644 --- a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.html +++ b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.html @@ -40,7 +40,11 @@
- +
diff --git a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.ts b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.ts index 68f3e7995..20d164b6b 100644 --- a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.ts +++ b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.ts @@ -23,6 +23,7 @@ import { TranslateService } from '@ngx-translate/core'; import { FileStatusWrapper } from '../model/file-status.wrapper'; import { PermissionsService } from '../../../common/service/permissions.service'; import { Subscription, timer } from 'rxjs'; +import { processFilters } from '../../../common/filter/utils/filter-utils'; const KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown']; @@ -48,7 +49,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy { selectedAnnotation: AnnotationWrapper; pagesPanelActive = true; viewReady = false; - filters: FilterModel[]; + annotationFilters: FilterModel[]; loadingMessage: string; canPerformAnnotationActions: boolean; @@ -160,8 +161,9 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy { this.activeViewer.annotManager.deleteAnnotations(existingAnnotations, true, true); } this.annotations = this.fileData.getAnnotations(this.appStateService.dictionaryData, this.permissionsService.currentUser); - this.filters = this._annotationProcessingService.getAnnotationFilter(this.annotations); - this.filtersChanged(this.filters); + const annotationFilters = this._annotationProcessingService.getAnnotationFilter(this.annotations); + this.annotationFilters = processFilters(this.annotationFilters, annotationFilters); + this.filtersChanged(this.annotationFilters); } handleAnnotationSelected(annotationId: string) { diff --git a/apps/red-ui/src/app/screens/file/model/annotation.wrapper.ts b/apps/red-ui/src/app/screens/file/model/annotation.wrapper.ts index 3ea57419e..bd020ac4f 100644 --- a/apps/red-ui/src/app/screens/file/model/annotation.wrapper.ts +++ b/apps/red-ui/src/app/screens/file/model/annotation.wrapper.ts @@ -1,6 +1,7 @@ import { Comment, IdRemoval, ManualRedactionEntry, Point, Rectangle, RedactionLogEntry, TypeValue } from '@redaction/red-ui-http'; import { UserWrapper } from '../../../user/user.service'; import { FileStatusWrapper } from './file-status.wrapper'; +import { humanize } from '../../../utils/functions'; export const SuperTypeSorter = { 'add-dictionary': 2, @@ -149,7 +150,11 @@ export class AnnotationWrapper { AnnotationWrapper._setSuperType(annotationWrapper, redactionLogEntry, manualRedactionEntry, idRemoval); - annotationWrapper.typeLabel = 'annotation-type.' + annotationWrapper.superType; + if (annotationWrapper.superType === 'hint' || annotationWrapper.superType === 'redaction') { + annotationWrapper.typeLabel = humanize(annotationWrapper.dictionary); + } else { + annotationWrapper.typeLabel = 'annotation-type.' + annotationWrapper.superType; + } return annotationWrapper; } diff --git a/apps/red-ui/src/app/screens/file/model/file-status.wrapper.ts b/apps/red-ui/src/app/screens/file/model/file-status.wrapper.ts index e99c415bc..01715c51c 100644 --- a/apps/red-ui/src/app/screens/file/model/file-status.wrapper.ts +++ b/apps/red-ui/src/app/screens/file/model/file-status.wrapper.ts @@ -48,7 +48,7 @@ export class FileStatusWrapper { } get hasRequests() { - return this.fileStatus.hasRequests; + return this.fileStatus.hasRequests || this.hasUnappliedSuggestions; } get hasNone() { diff --git a/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.ts b/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.ts index 5dda93461..aa557fc1c 100644 --- a/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.ts +++ b/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.ts @@ -11,6 +11,7 @@ import { annotationFilterChecker, dueDateChecker, getFilteredEntities, + processFilters, projectMemberChecker, projectStatusChecker, RedactionFilterSorter @@ -38,6 +39,8 @@ export class ProjectListingScreenComponent implements OnInit, OnDestroy { public detailsContainerFilters: { statusFilters: FilterModel[]; + } = { + statusFilters: [] }; public displayedProjects: ProjectWrapper[] = []; @@ -155,9 +158,11 @@ export class ProjectListingScreenComponent implements OnInit, OnDestroy { .map((status) => ({ length: obj[status], color: status })); } - public reanalyseProject($event: MouseEvent, project: Project) { + async reanalyseProject($event: MouseEvent, project: Project) { $event.stopPropagation(); - this.appStateService.reanalyseProject(project); + await this.appStateService.reanalyzeProject(project); + await this.appStateService.loadAllProjects(); + this._calculateData(); } private _computeAllFilters() { @@ -187,29 +192,32 @@ export class ProjectListingScreenComponent implements OnInit, OnDestroy { }); }); - this.statusFilters = []; + const statusFilters = []; allDistinctFileStatus.forEach((status) => { - this.statusFilters.push({ + statusFilters.push({ key: status, label: humanize(status) }); }); + this.statusFilters = processFilters(this.statusFilters, statusFilters); - this.peopleFilters = []; + const peopleFilters = []; allDistinctPeople.forEach((userId) => { - this.peopleFilters.push({ + peopleFilters.push({ key: userId, label: this.userService.getNameForId(userId) }); }); + this.peopleFilters = processFilters(this.peopleFilters, peopleFilters); - this.dueDateFilters = []; + const dueDateFilters = []; allDistinctDueDates.forEach((date) => { - this.dueDateFilters.push({ + dueDateFilters.push({ key: date, label: date }); }); + this.dueDateFilters = processFilters(this.dueDateFilters, dueDateFilters); const needsWorkFilters = []; allDistinctNeedsWork.forEach((type) => { @@ -220,7 +228,7 @@ export class ProjectListingScreenComponent implements OnInit, OnDestroy { }); needsWorkFilters.sort((a, b) => RedactionFilterSorter[a.key] - RedactionFilterSorter[b.key]); - this.needsWorkFilters = needsWorkFilters; + this.needsWorkFilters = processFilters(this.needsWorkFilters, needsWorkFilters); } filtersChanged(filters?: { [key: string]: FilterModel[] }): void { @@ -249,6 +257,8 @@ export class ProjectListingScreenComponent implements OnInit, OnDestroy { } openEditProjectDialog($event: MouseEvent, project: Project) { - this._dialogService.openEditProjectDialog($event, project); + this._dialogService.openEditProjectDialog($event, project, () => { + this._calculateData(); + }); } } diff --git a/apps/red-ui/src/app/screens/project-overview-screen/project-details/project-details.component.ts b/apps/red-ui/src/app/screens/project-overview-screen/project-details/project-details.component.ts index a3af1af6d..9424d859a 100644 --- a/apps/red-ui/src/app/screens/project-overview-screen/project-details/project-details.component.ts +++ b/apps/red-ui/src/app/screens/project-overview-screen/project-details/project-details.component.ts @@ -55,7 +55,6 @@ export class ProjectDetailsComponent implements OnInit { this._dialogService.openAssignProjectMembersAndOwnerDialog(null, this.appStateService.activeProject.project, () => { this.reloadProjects.emit(); this._changeDetectorRef.detectChanges(); - console.log(this.appStateService.activeProject); }); } diff --git a/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.html b/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.html index 6547ce0aa..4328d28f1 100644 --- a/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.html +++ b/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.html @@ -201,7 +201,7 @@
diff --git a/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.ts b/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.ts index f94a68774..3a47bec91 100644 --- a/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.ts +++ b/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.ts @@ -14,7 +14,7 @@ import { FilterModel } from '../../common/filter/model/filter.model'; import * as moment from 'moment'; import { ProjectDetailsComponent } from './project-details/project-details.component'; import { FileStatusWrapper } from '../file/model/file-status.wrapper'; -import { annotationFilterChecker, getFilteredEntities, keyChecker, RedactionFilterSorter } from '../../common/filter/utils/filter-utils'; +import { annotationFilterChecker, getFilteredEntities, keyChecker, processFilters, RedactionFilterSorter } from '../../common/filter/utils/filter-utils'; import { SortingOption, SortingService } from '../../utils/sorting.service'; import { PermissionsService } from '../../common/service/permissions.service'; import { UserService } from '../../user/user.service'; @@ -38,7 +38,7 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy { detailsContainerFilters: { needsWorkFilters: FilterModel[]; statusFilters: FilterModel[]; - }; + } = { needsWorkFilters: [], statusFilters: [] }; @ViewChild('projectDetailsComponent', { static: false }) private _projectDetailsComponent: ProjectDetailsComponent; @@ -104,11 +104,7 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy { actions: [ { title: this._translateService.instant('project-overview.new-rule.toast.actions.reanalyse-all'), - action: () => - this.appStateService - .reanalyzeProject() - .toPromise() - .then(() => this.reloadProjects()) + action: () => this.appStateService.reanalyzeProject().then(() => this.reloadProjects()) }, { title: this._translateService.instant('project-overview.new-rule.toast.actions.later') @@ -226,26 +222,28 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy { if (file.hasNone) allDistinctNeedsWork.add('none'); }); - this.statusFilters = []; + const statusFilters = []; allDistinctFileStatusWrapper.forEach((status) => { - this.statusFilters.push({ + statusFilters.push({ key: status, label: humanize(status) }); }); + this.statusFilters = processFilters(this.statusFilters, statusFilters); - this.peopleFilters = []; + const peopleFilters = []; if (allDistinctPeople.has(undefined) || allDistinctPeople.has(null)) { allDistinctPeople.delete(undefined); allDistinctPeople.delete(null); allDistinctPeople.add(null); } allDistinctPeople.forEach((userId) => { - this.peopleFilters.push({ + peopleFilters.push({ key: userId, label: userId ? this.userService.getNameForId(userId) : this._translateService.instant('initials-avatar.unassigned') }); }); + this.peopleFilters = processFilters(this.peopleFilters, peopleFilters); const needsWorkFilters = []; allDistinctNeedsWork.forEach((type) => { @@ -255,7 +253,7 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy { }); }); needsWorkFilters.sort((a, b) => RedactionFilterSorter[a.key] - RedactionFilterSorter[b.key]); - this.needsWorkFilters = needsWorkFilters; + this.needsWorkFilters = processFilters(this.needsWorkFilters, needsWorkFilters); } filtersChanged(filters?: { [key: string]: FilterModel[] }): void { @@ -279,7 +277,12 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy { const filters = [ { values: this.statusFilters, checker: keyChecker('status') }, { values: this.peopleFilters, checker: keyChecker('currentReviewer') }, - { values: this.needsWorkFilters, checker: annotationFilterChecker, matchAll: true, checkerArgs: this.permissionsService } + { + values: this.needsWorkFilters, + checker: annotationFilterChecker, + matchAll: true, + checkerArgs: this.permissionsService + } ]; this.displayedFiles = getFilteredEntities(this.appStateService.activeProject.files, filters); this.detailsContainerFilters = { diff --git a/apps/red-ui/src/app/state/app-state.service.ts b/apps/red-ui/src/app/state/app-state.service.ts index ee558911c..a911ad7be 100644 --- a/apps/red-ui/src/app/state/app-state.service.ts +++ b/apps/red-ui/src/app/state/app-state.service.ts @@ -85,13 +85,6 @@ export class AppStateService { return this._dictionaryData; } - reanalyzeProject(project?: Project) { - if (!project) { - project = this.activeProject.project; - } - return this._reanalysisControllerService.reanalyzeProject(project.projectId); - } - getDictionaryColor(type?: string) { const color = this._dictionaryData[type]?.hexColor; return color ? color : this._dictionaryData['default'].hexColor; @@ -233,11 +226,11 @@ export class AppStateService { return files; } - reanalyseProject(project?: Project) { + async reanalyzeProject(project?: Project) { if (!project) { project = this.activeProject.project; } - this._reanalysisControllerService.reanalyzeProject(project.projectId).subscribe(() => {}); + await this._reanalysisControllerService.reanalyzeProject(project.projectId).toPromise(); } downloadRedactionReport(project?: Project) { diff --git a/apps/red-ui/src/assets/i18n/en.json b/apps/red-ui/src/assets/i18n/en.json index 34eb09a2f..afe37454d 100644 --- a/apps/red-ui/src/assets/i18n/en.json +++ b/apps/red-ui/src/assets/i18n/en.json @@ -85,10 +85,10 @@ }, "stats": { "analyzed-pages": "Analyzed pages", - "total-people": "Total users", + "total-people": "Total user(s)", "charts": { "projects": "Projects", - "total-documents": "Total Documents" + "total-documents": "Total Document(s)" } }, "add-edit-dialog": { @@ -205,7 +205,7 @@ "stats": { "documents": "{{count}} documents", "analysed-pages": "{{count}} analyzed pages", - "people": "{{count}} people", + "people": "{{count}} user(s)", "created-on": "Created on {{date}}", "due-date": "Due {{date}}" }, @@ -236,17 +236,21 @@ "label": "Workload" } }, - "reviewer": "Assignee", + "reviewer": "Assigned to", "unassigned": "Unassigned" }, "annotation-actions": { "accept-suggestion": { - "label": "Approve", + "label": "Accept Suggestion", "add-to-dict": "Approve and add to dictionary", "remove-from-dict": "Approve and remove from dictionary", "only-here": "Approve only here" }, + "suggest-remove-annotation": "Remove or Suggest to remove this entry", + "reject-suggestion": "Reject Suggestion", "remove-annotation": { + "suggest-remove-from-dict": "Suggest to remove from dictionary", + "suggest-only-here": "Suggest to remove only here", "remove-from-dict": "Remove from dictionary", "only-here": "Remove only here" }, @@ -390,5 +394,11 @@ "success": "Redaction added.", "error": "Failed to add redaction." } + }, + "confirmation-dialog": { + "delete-file": { + "title": "Confirm deletion", + "question": "Do you wish to proceed?" + } } }