Merge branch 'VM/FileAttributes'
This commit is contained in:
commit
75cf938abe
@ -57,6 +57,9 @@ export class FileStatusWrapper implements FileStatus {
|
||||
this.primaryAttribute = '-';
|
||||
}
|
||||
}
|
||||
if (!this.fileAttributes || !this.fileAttributes.attributeIdToValue) {
|
||||
this.fileAttributes = { attributeIdToValue: {} };
|
||||
}
|
||||
}
|
||||
|
||||
readonly excludedPagesCount = this.excludedPages?.length ?? 0;
|
||||
@ -67,7 +70,7 @@ export class FileStatusWrapper implements FileStatus {
|
||||
readonly hintsOnly = this.hasHints && !this.hasRedactions;
|
||||
readonly hasNone = !this.hasRedactions && !this.hasHints && !this.hasSuggestions;
|
||||
|
||||
readonly isUnassigned = !this.currentReviewer
|
||||
readonly isUnassigned = !this.currentReviewer;
|
||||
readonly isError = this.status === FileStatus.StatusEnum.ERROR;
|
||||
readonly isProcessing = processingStatuses.includes(this.status);
|
||||
readonly isApproved = this.status === FileStatus.StatusEnum.APPROVED;
|
||||
|
||||
@ -51,6 +51,18 @@
|
||||
{{ 'add-edit-file-attribute.form.primary' | translate }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="red-input-group mt-0">
|
||||
<mat-checkbox color="primary" formControlName="filterable" name="filterable">
|
||||
{{ 'add-edit-file-attribute.form.filterable' | translate }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="red-input-group mt-0">
|
||||
<mat-checkbox color="primary" formControlName="displayedInFileList" name="displayedInFileList">
|
||||
{{ 'add-edit-file-attribute.form.displayedInFileList' | translate }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dialog-actions">
|
||||
|
||||
@ -32,7 +32,9 @@ export class AddEditFileAttributeDialogComponent {
|
||||
csvColumnHeader: [this.fileAttribute?.csvColumnHeader, Validators.required],
|
||||
type: [this.fileAttribute?.type || FileAttributeConfig.TypeEnum.TEXT, Validators.required],
|
||||
readonly: [this.fileAttribute ? !this.fileAttribute.editable : false],
|
||||
primaryAttribute: [this.fileAttribute?.primaryAttribute]
|
||||
primaryAttribute: [this.fileAttribute?.primaryAttribute],
|
||||
filterable: [this.fileAttribute?.filterable],
|
||||
displayedInFileList: [this.fileAttribute?.displayedInFileList]
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -62,6 +62,12 @@
|
||||
<div class="small-label">
|
||||
{{ attribute.csvColumnHeader }}
|
||||
</div>
|
||||
<div class="center">
|
||||
<iqser-round-checkbox *ngIf="attribute.filterable" [active]="true" [size]="18"></iqser-round-checkbox>
|
||||
</div>
|
||||
<div class="center">
|
||||
<iqser-round-checkbox *ngIf="attribute.displayedInFileList" [active]="true" [size]="18"></iqser-round-checkbox>
|
||||
</div>
|
||||
<div class="center">
|
||||
<iqser-round-checkbox *ngIf="attribute.primaryAttribute" [active]="true" [size]="18"></iqser-round-checkbox>
|
||||
</div>
|
||||
|
||||
@ -11,7 +11,7 @@ iqser-table-header::ng-deep .header-item {
|
||||
|
||||
.content-container cdk-virtual-scroll-viewport {
|
||||
::ng-deep.cdk-virtual-scroll-content-wrapper {
|
||||
grid-template-columns: auto 2fr 1fr 1fr 1fr 1fr 1fr 11px;
|
||||
grid-template-columns: auto 2fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 11px;
|
||||
|
||||
.table-item {
|
||||
> div:not(.scrollbar-placeholder) {
|
||||
@ -36,7 +36,7 @@ iqser-table-header::ng-deep .header-item {
|
||||
}
|
||||
|
||||
&.has-scrollbar:hover ::ng-deep.cdk-virtual-scroll-content-wrapper {
|
||||
grid-template-columns: auto 2fr 1fr 1fr 1fr 1fr 1fr;
|
||||
grid-template-columns: auto 2fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -37,6 +37,14 @@ export class FileAttributesListingScreenComponent extends ListingComponent<FileA
|
||||
class: 'flex-center'
|
||||
},
|
||||
{ label: _('file-attributes-listing.table-col-names.csv-column') },
|
||||
{
|
||||
label: _('file-attributes-listing.table-col-names.filterable'),
|
||||
class: 'flex-center'
|
||||
},
|
||||
{
|
||||
label: _('file-attributes-listing.table-col-names.displayed-in-file-list'),
|
||||
class: 'flex-center'
|
||||
},
|
||||
{
|
||||
label: _('file-attributes-listing.table-col-names.primary'),
|
||||
class: 'flex-center',
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
<section *ngIf="!!currentDossier">
|
||||
<redaction-page-header
|
||||
[actionConfigs]="actionConfigs"
|
||||
[fileAttributeConfigs]="fileAttributeConfigs"
|
||||
[searchPlaceholder]="'dossier-overview.search' | translate"
|
||||
[showCloseButton]="true"
|
||||
>
|
||||
@ -104,6 +105,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngFor="let config of displayedInFileListAttributes">
|
||||
{{ fileStatus.fileAttributes.attributeIdToValue[config.id] }}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div [class.error]="fileStatus.isError" class="small-label">
|
||||
{{ fileStatus.added | date: 'd MMM. yyyy, hh:mm a' }}
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
@import '../../../../../assets/styles/variables';
|
||||
@import '../../../../../assets/styles/red-mixins';
|
||||
|
||||
:root {
|
||||
--dynamic-columns: '0';
|
||||
}
|
||||
|
||||
.file-upload-input {
|
||||
display: none;
|
||||
}
|
||||
@ -17,7 +21,7 @@ iqser-table-column-name::ng-deep {
|
||||
|
||||
cdk-virtual-scroll-viewport {
|
||||
::ng-deep.cdk-virtual-scroll-content-wrapper {
|
||||
grid-template-columns: auto 3fr 2fr 1fr 2fr 1fr auto 11px;
|
||||
grid-template-columns: auto 3fr repeat(var(--dynamic-columns), 1fr) 2fr 1fr 2fr 1fr auto 11px;
|
||||
|
||||
.table-item {
|
||||
> div {
|
||||
@ -67,7 +71,7 @@ cdk-virtual-scroll-viewport {
|
||||
|
||||
&.has-scrollbar:hover {
|
||||
::ng-deep.cdk-virtual-scroll-content-wrapper {
|
||||
grid-template-columns: auto 3fr 2fr 1fr 2fr 1fr auto;
|
||||
grid-template-columns: auto 3fr repeat(var(--dynamic-columns), 1fr) 2fr 1fr 2fr 1fr auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,15 @@
|
||||
import { ChangeDetectorRef, Component, ElementRef, HostListener, Injector, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ElementRef,
|
||||
HostListener,
|
||||
Injector,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
Renderer2,
|
||||
TemplateRef,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import { Toaster } from '@services/toaster.service';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { FileDropOverlayService } from '@upload-download/services/file-drop-overlay.service';
|
||||
@ -30,6 +41,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { annotationFilterChecker } from '@shared/components/filters/popup-filter/utils/filter-utils';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
||||
import { FileAttributeConfig } from '@redaction/red-ui-http';
|
||||
|
||||
@Component({
|
||||
templateUrl: './dossier-overview-screen.component.html',
|
||||
@ -50,7 +62,7 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
|
||||
hide: !this.currentUser.isManager
|
||||
}
|
||||
];
|
||||
readonly tableColumnConfigs: readonly TableColumnConfig<FileStatusWrapper>[] = [
|
||||
private readonly _defaultTableConfigs: readonly TableColumnConfig<FileStatusWrapper>[] = [
|
||||
{
|
||||
label: _('dossier-overview.table-col-names.name'),
|
||||
sortByKey: 'filename'
|
||||
@ -77,9 +89,13 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
|
||||
sortByKey: 'statusSort'
|
||||
}
|
||||
];
|
||||
|
||||
tableColumnConfigs: readonly TableColumnConfig<FileStatusWrapper>[] = [];
|
||||
collapsedDetails = false;
|
||||
dossierAttributes: DossierAttributeWithValue[] = [];
|
||||
fileAttributeConfigs: FileAttributeConfig[];
|
||||
protected readonly _primaryKey = 'filename';
|
||||
dynamicColumnsCount = 0;
|
||||
|
||||
@ViewChild(DossierDetailsComponent, { static: false })
|
||||
private readonly _dossierDetailsComponent: DossierDetailsComponent;
|
||||
@ -105,10 +121,14 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
|
||||
private readonly _statusOverlayService: StatusOverlayService,
|
||||
private readonly _userPreferenceService: UserPreferenceService,
|
||||
private readonly _fileDropOverlayService: FileDropOverlayService,
|
||||
private readonly _dossierAttributesService: DossierAttributesService
|
||||
private readonly _dossierAttributesService: DossierAttributesService,
|
||||
private readonly _renderer: Renderer2,
|
||||
private readonly _elementRef: ElementRef
|
||||
) {
|
||||
super(_injector);
|
||||
this._loadEntitiesFromState();
|
||||
|
||||
this.fileAttributeConfigs = this._appStateService.activeFileAttributesConfig.fileAttributeConfigs;
|
||||
}
|
||||
|
||||
get checkedRequiredFilters() {
|
||||
@ -141,6 +161,7 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
|
||||
|
||||
this.dossierAttributes = await this._dossierAttributesService.getValues(this.currentDossier);
|
||||
this.searchService.setSearchKey('filename');
|
||||
this._configureTableColumns();
|
||||
} catch (e) {
|
||||
} finally {
|
||||
this._loadingService.stop();
|
||||
@ -235,9 +256,25 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
|
||||
this.collapsedDetails = !this.collapsedDetails;
|
||||
}
|
||||
|
||||
get displayedInFileListAttributes() {
|
||||
return this.fileAttributeConfigs.filter(config => config.displayedInFileList);
|
||||
}
|
||||
|
||||
recentlyModifiedChecker = (file: FileStatusWrapper) =>
|
||||
moment(file.lastUpdated).add(this._appConfigService.getConfig(AppConfigKey.RECENT_PERIOD_IN_HOURS), 'hours').isAfter(moment());
|
||||
|
||||
private _configureTableColumns() {
|
||||
const dynamicColumns: TableColumnConfig<FileStatusWrapper>[] = [];
|
||||
for (const config of this.displayedInFileListAttributes) {
|
||||
if (config.displayedInFileList) {
|
||||
dynamicColumns.push({ label: config.label, notTranslatable: true });
|
||||
}
|
||||
}
|
||||
this.tableColumnConfigs = [this._defaultTableConfigs[0], ...dynamicColumns, ...this._defaultTableConfigs.slice(1)];
|
||||
this.dynamicColumnsCount = dynamicColumns.length;
|
||||
(this._elementRef.nativeElement as HTMLElement).style.setProperty('--dynamic-columns', `${this.dynamicColumnsCount}`);
|
||||
}
|
||||
|
||||
private _loadEntitiesFromState() {
|
||||
this.currentDossier = this._appStateService.activeDossier;
|
||||
if (this.currentDossier) this.entitiesService.setEntities(this.currentDossier.files);
|
||||
@ -256,6 +293,8 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
|
||||
const allDistinctAddedDates = new Set<string>();
|
||||
const allDistinctNeedsWork = new Set<string>();
|
||||
|
||||
const dynamicFilters = new Map<string, Set<string>>();
|
||||
|
||||
this.entitiesService.all.forEach(file => {
|
||||
allDistinctPeople.add(file.currentReviewer);
|
||||
allDistinctFileStatusWrapper.add(file.status);
|
||||
@ -269,6 +308,24 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
|
||||
if (file.hasImages) allDistinctNeedsWork.add('image');
|
||||
if (file.hasNone) allDistinctNeedsWork.add('none');
|
||||
if (file.hasAnnotationComments) allDistinctNeedsWork.add('comment');
|
||||
|
||||
// extract values for dynamic filters
|
||||
this.fileAttributeConfigs.forEach(config => {
|
||||
if (config.filterable) {
|
||||
const filterKey = `${config.id}:${config.label}`;
|
||||
let filters = dynamicFilters.get(filterKey);
|
||||
if (!filters) {
|
||||
dynamicFilters.set(filterKey, new Set<string>());
|
||||
filters = dynamicFilters.get(filterKey);
|
||||
}
|
||||
let filterValue = file.fileAttributes?.attributeIdToValue[config.id];
|
||||
if (!filterValue) {
|
||||
filterValue = this._translateService.instant('filters.empty');
|
||||
file.fileAttributes.attributeIdToValue[config.id] = '-';
|
||||
}
|
||||
filters.add(filterValue);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const statusFilters = [...allDistinctFileStatusWrapper].map<NestedFilter>(item => ({
|
||||
@ -322,6 +379,18 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
|
||||
matchAll: true
|
||||
});
|
||||
|
||||
dynamicFilters.forEach((filterValue: Set<string>, filterKey: string) => {
|
||||
const id = filterKey.split(':')[0];
|
||||
const key = filterKey.split(':')[1];
|
||||
this.filterService.addFilterGroup({
|
||||
slug: key,
|
||||
label: key,
|
||||
icon: 'red:template',
|
||||
filters: [...filterValue].map<NestedFilter>((value: string) => ({ key: value, label: value })),
|
||||
checker: (input: FileStatusWrapper, filter: NestedFilter) => filter.label === input.fileAttributes.attributeIdToValue[id]
|
||||
});
|
||||
});
|
||||
|
||||
this.filterService.addFilterGroup({
|
||||
slug: 'quickFilters',
|
||||
filters: this._createQuickFilters(),
|
||||
|
||||
@ -5,6 +5,7 @@ import { FilterService, SearchService } from '@iqser/common-ui';
|
||||
import { distinctUntilChanged, map } from 'rxjs/operators';
|
||||
import { combineLatest, Observable, of } from 'rxjs';
|
||||
import { SearchPosition, SearchPositions } from '@shared/components/page-header/models/search-positions.type';
|
||||
import { FileAttributeConfig } from '@redaction/red-ui-http';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-page-header',
|
||||
@ -18,6 +19,7 @@ export class PageHeaderComponent<T> {
|
||||
@Input() showCloseButton: boolean;
|
||||
@Input() actionConfigs: readonly ActionConfig[];
|
||||
@Input() buttonConfigs: readonly ButtonConfig[];
|
||||
@Input() fileAttributeConfigs: readonly FileAttributeConfig[];
|
||||
@Input() searchPlaceholder: string;
|
||||
@Input() searchWidth: number | 'full';
|
||||
@Input() searchPosition: SearchPosition = SearchPositions.afterFilters;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"OAUTH_URL": "https://red-staging.iqser.cloud/auth/realms/redaction",
|
||||
"API_URL": "https://red-staging.iqser.cloud/redaction-gateway-v1",
|
||||
"OAUTH_URL": "https://dom1.iqser.cloud/auth/realms/redaction",
|
||||
"API_URL": "https://dom1.iqser.cloud/redaction-gateway-v1",
|
||||
"OAUTH_CLIENT_ID": "redaction",
|
||||
"BACKEND_APP_VERSION": "4.4.40",
|
||||
"FRONTEND_APP_VERSION": "1.1",
|
||||
|
||||
@ -82,6 +82,8 @@
|
||||
"form": {
|
||||
"column-header": "CSV Column Header",
|
||||
"column-header-placeholder": "Enter CSV Column Header",
|
||||
"displayedInFileList": "Displayed In File List",
|
||||
"filterable": "Filterable",
|
||||
"name": "Attribute Name",
|
||||
"name-placeholder": "Enter Name",
|
||||
"primary": "Set as Primary",
|
||||
@ -928,6 +930,8 @@
|
||||
"search": "Search by attribute name...",
|
||||
"table-col-names": {
|
||||
"csv-column": "CSV Column",
|
||||
"displayed-in-file-list": "Dis. In File List",
|
||||
"filterable": "Filterable",
|
||||
"name": "Name",
|
||||
"primary": "Primary",
|
||||
"primary-info-tooltip": "The value of the attribute set as primary shows up under the file name in the documents list.",
|
||||
@ -1040,7 +1044,8 @@
|
||||
"filter-by": "Filter:",
|
||||
"needs-work": "Workload",
|
||||
"people": "Dossier Member(s)",
|
||||
"status": "Status"
|
||||
"status": "Status",
|
||||
"empty": "Empty"
|
||||
},
|
||||
"general-config-screen": {
|
||||
"actions": {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user