move listing component, add table header to remaining tables

This commit is contained in:
Dan Percic 2021-08-10 17:04:07 +03:00
parent a95362ba73
commit 62fa054697
36 changed files with 532 additions and 720 deletions

View File

@ -2,8 +2,7 @@ import { Component, Injector, OnInit } from '@angular/core';
import { FileDownloadService } from '@upload-download/services/file-download.service';
import { DownloadStatusWrapper } from '@upload-download/model/download-status.wrapper';
import { DownloadControllerService } from '@redaction/red-ui-http';
import { BaseListingComponent, DefaultListingServices } from '@shared/base/base-listing.component';
import { CircleButtonTypes, TableColumnConfig } from '@iqser/common-ui';
import { CircleButtonTypes, DefaultListingServices, ListingComponent, TableColumnConfig } from '@iqser/common-ui';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { LoadingService } from '@services/loading.service';
@ -13,7 +12,7 @@ import { LoadingService } from '@services/loading.service';
styleUrls: ['./downloads-list-screen.component.scss'],
providers: [...DefaultListingServices]
})
export class DownloadsListScreenComponent extends BaseListingComponent<DownloadStatusWrapper> implements OnInit {
export class DownloadsListScreenComponent extends ListingComponent<DownloadStatusWrapper> implements OnInit {
readonly itemSize = 80;
protected readonly _primaryKey = 'storageId';
readonly circleButtonTypes = CircleButtonTypes;

View File

@ -8,13 +8,13 @@ import { Toaster } from '@services/toaster.service';
import { DossierAttributesService } from '@shared/services/controller-wrappers/dossier-attributes.service';
import { dossierAttributeTypesTranslations } from '../../translations/dossier-attribute-types-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { AutoUnsubscribeComponent } from '@iqser/common-ui';
import { AutoUnsubscribe } from '@iqser/common-ui';
@Component({
templateUrl: './add-edit-dossier-attribute-dialog.component.html',
styleUrls: ['./add-edit-dossier-attribute-dialog.component.scss']
})
export class AddEditDossierAttributeDialogComponent extends AutoUnsubscribeComponent implements OnDestroy {
export class AddEditDossierAttributeDialogComponent extends AutoUnsubscribe implements OnDestroy {
dossierAttributeForm: FormGroup;
dossierAttribute: DossierAttributeConfig;
readonly translations = dossierAttributeTypesTranslations;

View File

@ -1,13 +1,99 @@
<div class="header-item">
<iqser-round-checkbox
(click)="toggleSelectAll()"
[active]="entitiesService.areAllSelected$ | async"
[indeterminate]="entitiesService.notAllSelected$ | async"
></iqser-round-checkbox>
<span class="all-caps-label">
{{ 'file-attributes-csv-import.table-header.title' | translate: { length: (entitiesService.allLength$ | async) } }}
</span>
<redaction-table-header
[bulkActions]="bulkActions"
[selectionEnabled]="true"
[hasEmptyColumn]="true"
[tableColumnConfigs]="tableColumnConfigs"
[tableHeaderLabel]="tableHeaderLabel"
></redaction-table-header>
<redaction-empty-state
*ngIf="entitiesService.noData$ | async"
[text]="'file-attributes-csv-import.no-data.title' | translate"
icon="red:attribute"
></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="50" redactionHasScrollbar>
<div
(mouseenter)="setHoveredColumn.emit(field.csvColumn)"
(mouseleave)="setHoveredColumn.emit()"
*cdkVirtualFor="let field of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey"
class="table-item"
>
<div (click)="toggleEntitySelected($event, field)" class="selection-column">
<iqser-round-checkbox [active]="isSelected(field)"></iqser-round-checkbox>
</div>
<div [class.editing]="field.editingName" class="name">
<div *ngIf="!field.editingName">
{{ field.name }}
</div>
<form (submit)="field.editingName = false; field.name = field.temporaryName" *ngIf="field.editingName">
<div class="red-input-group w-200">
<input [(ngModel)]="field.temporaryName" name="name" />
</div>
</form>
<iqser-circle-button
(action)="field.editingName = true"
*ngIf="!field.editingName"
[tooltip]="'file-attributes-csv-import.action.edit-name' | translate"
class="edit-name-button"
icon="red:edit"
[type]="circleButtonTypes.dark"
></iqser-circle-button>
<ng-container *ngIf="field.editingName">
<iqser-circle-button
(action)="field.editingName = false; field.name = field.temporaryName"
[tooltip]="'file-attributes-csv-import.action.save-name' | translate"
icon="red:check"
[type]="circleButtonTypes.dark"
></iqser-circle-button>
<iqser-circle-button
(action)="field.editingName = false; field.temporaryName = field.name"
[tooltip]="'file-attributes-csv-import.action.cancel-edit-name' | translate"
icon="red:close"
[type]="circleButtonTypes.dark"
></iqser-circle-button>
</ng-container>
</div>
<div>
<div class="red-input-group">
<mat-form-field class="no-label">
<mat-select [(ngModel)]="field.type">
<mat-option *ngFor="let type of typeOptions" [value]="type">
{{ translations[type] | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
<div class="center">
<mat-slide-toggle [(ngModel)]="field.readonly" color="primary"></mat-slide-toggle>
</div>
<div class="center">
<iqser-round-checkbox (click)="togglePrimary(field)" [active]="field.primaryAttribute"></iqser-round-checkbox>
</div>
<div class="actions-container">
<div class="action-buttons">
<iqser-circle-button
(action)="field.primaryAttribute = false; toggleFieldActive.emit(field)"
[removeTooltip]="true"
[tooltip]="'file-attributes-csv-import.action.remove' | translate"
icon="red:trash"
[type]="circleButtonTypes.dark"
></iqser-circle-button>
</div>
</div>
<div class="scrollbar-placeholder"></div>
</div>
</cdk-virtual-scroll-viewport>
<ng-template #bulkActions>
<ng-container *ngIf="entitiesService.areSomeSelected$ | async">
<iqser-circle-button
[matMenuTriggerFor]="readOnlyMenu"
@ -49,109 +135,4 @@
</button>
</mat-menu>
</ng-container>
</div>
<div class="table-header" redactionSyncWidth="table-item">
<div class="select-oval-placeholder"></div>
<iqser-table-column-name [label]="'file-attributes-csv-import.table-col-names.name' | translate" class="name"></iqser-table-column-name>
<iqser-table-column-name [label]="'file-attributes-csv-import.table-col-names.type' | translate"></iqser-table-column-name>
<iqser-table-column-name
[label]="'file-attributes-csv-import.table-col-names.read-only' | translate"
class="flex-center"
leftIcon="red:read-only"
></iqser-table-column-name>
<iqser-table-column-name
[label]="'file-attributes-csv-import.table-col-names.primary' | translate"
[rightIconTooltip]="'file-attributes-csv-import.table-col-names.primary-info-tooltip' | translate"
class="flex-center"
rightIcon="red:status-info"
></iqser-table-column-name>
<div></div>
<div class="scrollbar-placeholder"></div>
</div>
<redaction-empty-state
*ngIf="entitiesService.noData$ | async"
[text]="'file-attributes-csv-import.no-data.title' | translate"
icon="red:attribute"
></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="50" redactionHasScrollbar>
<!-- Table lines -->
<div
(mouseenter)="setHoveredColumn.emit(field.csvColumn)"
(mouseleave)="setHoveredColumn.emit()"
*cdkVirtualFor="let field of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey"
class="table-item"
>
<div (click)="toggleEntitySelected($event, field)" class="selection-column">
<iqser-round-checkbox [active]="isSelected(field)"></iqser-round-checkbox>
</div>
<div [class.editing]="field.editingName" class="name">
<div *ngIf="!field.editingName">
{{ field.name }}
</div>
<form (submit)="field.editingName = false; field.name = field.temporaryName" *ngIf="field.editingName">
<div class="red-input-group w-200">
<input [(ngModel)]="field.temporaryName" name="name" />
</div>
</form>
<iqser-circle-button
(action)="field.editingName = true"
*ngIf="!field.editingName"
[tooltip]="'file-attributes-csv-import.action.edit-name' | translate"
class="edit-name-button"
icon="red:edit"
[type]="circleButtonTypes.dark"
></iqser-circle-button>
<ng-container *ngIf="field.editingName">
<iqser-circle-button
(action)="field.editingName = false; field.name = field.temporaryName"
[tooltip]="'file-attributes-csv-import.action.save-name' | translate"
icon="red:check"
[type]="circleButtonTypes.dark"
></iqser-circle-button>
<iqser-circle-button
(action)="field.editingName = false; field.temporaryName = field.name"
[tooltip]="'file-attributes-csv-import.action.cancel-edit-name' | translate"
icon="red:close"
[type]="circleButtonTypes.dark"
></iqser-circle-button>
</ng-container>
</div>
<div>
<div class="red-input-group">
<mat-form-field class="no-label">
<mat-select [(ngModel)]="field.type">
<mat-option *ngFor="let type of typeOptions" [value]="type">
{{ translations[type] | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
<div class="center">
<mat-slide-toggle [(ngModel)]="field.readonly" color="primary"></mat-slide-toggle>
</div>
<div class="center">
<iqser-round-checkbox (click)="togglePrimary(field)" [active]="field.primaryAttribute"></iqser-round-checkbox>
</div>
<div class="actions-container">
<div class="action-buttons">
<iqser-circle-button
(action)="field.primaryAttribute = false; toggleFieldActive.emit(field)"
[removeTooltip]="true"
[tooltip]="'file-attributes-csv-import.action.remove' | translate"
icon="red:trash"
[type]="circleButtonTypes.dark"
></iqser-circle-button>
</div>
</div>
<div class="scrollbar-placeholder"></div>
</div>
</cdk-virtual-scroll-viewport>
</ng-template>

View File

@ -1,16 +1,14 @@
@import '../../../../../../assets/styles/variables';
iqser-table-column-name::ng-deep {
> div {
padding: 0 13px 0 10px !important;
iqser-table-column-name::ng-deep > div {
padding: 0 13px 0 10px !important;
&.name {
padding-left: 22px !important;
}
&.name {
padding-left: 22px !important;
}
}
.header-item {
redaction-table-header::ng-deep .header-item {
padding: 0 24px 0 10px;
box-shadow: none;
border-top: 1px solid $separator;

View File

@ -1,9 +1,9 @@
import { Component, EventEmitter, Injector, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import { Field } from '../file-attributes-csv-import-dialog.component';
import { FileAttributeConfig } from '@redaction/red-ui-http';
import { CircleButtonTypes } from '@iqser/common-ui';
import { BaseListingComponent, DefaultListingServices } from '@shared/base/base-listing.component';
import { CircleButtonTypes, DefaultListingServices, ListingComponent, TableColumnConfig } from '@iqser/common-ui';
import { fileAttributeTypesTranslations } from '../../../translations/file-attribute-types-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
@Component({
selector: 'redaction-active-fields-listing',
@ -11,10 +11,29 @@ import { fileAttributeTypesTranslations } from '../../../translations/file-attri
styleUrls: ['./active-fields-listing.component.scss'],
providers: [...DefaultListingServices]
})
export class ActiveFieldsListingComponent extends BaseListingComponent<Field> implements OnChanges {
export class ActiveFieldsListingComponent extends ListingComponent<Field> implements OnChanges {
protected readonly _primaryKey = 'csvColumn';
readonly circleButtonTypes = CircleButtonTypes;
readonly translations = fileAttributeTypesTranslations;
readonly tableHeaderLabel = _('file-attributes-csv-import.table-header.title');
readonly tableColumnConfigs: TableColumnConfig<Field>[] = [
{
label: _('file-attributes-csv-import.table-col-names.name'),
class: 'name'
},
{ label: _('file-attributes-csv-import.table-col-names.type') },
{
label: _('file-attributes-csv-import.table-col-names.read-only'),
class: 'flex-center',
leftIcon: 'red:read-only'
},
{
label: _('file-attributes-csv-import.table-col-names.primary'),
class: 'flex-center',
rightIcon: 'red:status-info',
rightIconTooltip: _('file-attributes-csv-import.table-col-names.primary-info-tooltip')
}
];
readonly typeOptions = [
FileAttributeConfig.TypeEnum.TEXT,
FileAttributeConfig.TypeEnum.NUMBER,

View File

@ -4,7 +4,7 @@
<div class="dialog-content">
<div class="sub-header">
<div class="left">
<div class="info"><span translate="file-attributes-csv-import.file"> </span> {{ csvFile.name }}</div>
<div class="info"><span translate="file-attributes-csv-import.file"> </span> {{ data.csv.name }}</div>
<div class="large-label">
{{ 'file-attributes-csv-import.total-rows' | translate: { rows: parseResult?.data?.length } }}
</div>

View File

@ -1,14 +1,12 @@
import { Component, Inject, Injector } from '@angular/core';
import { AbstractControl, FormBuilder, FormGroup, ValidatorFn, Validators } from '@angular/forms';
import { AppStateService } from '@state/app-state.service';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import * as Papa from 'papaparse';
import { FileAttributeConfig, FileAttributesConfig, FileAttributesControllerService } from '@redaction/red-ui-http';
import { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators';
import { Toaster } from '@services/toaster.service';
import { TranslateService } from '@ngx-translate/core';
import { BaseListingComponent, DefaultListingServices } from '@shared/base/base-listing.component';
import { DefaultListingServices, ListingComponent, TableColumnConfig } from '@iqser/common-ui';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
export interface Field {
@ -27,15 +25,14 @@ export interface Field {
styleUrls: ['./file-attributes-csv-import-dialog.component.scss'],
providers: [...DefaultListingServices]
})
export class FileAttributesCsvImportDialogComponent extends BaseListingComponent<Field> {
export class FileAttributesCsvImportDialogComponent extends ListingComponent<Field> {
protected readonly _primaryKey = 'csvColumn';
readonly tableColumnConfigs: TableColumnConfig<Field>[] = [];
csvFile: File;
dossierTemplateId: string;
parseResult: { data: any[]; errors: any[]; meta: any; fields: Field[] };
hoveredColumn: string;
activeFields: Field[] = [];
baseConfigForm: FormGroup;
readonly baseConfigForm: FormGroup;
isSearchOpen = false;
previewExpanded = true;
filteredKeyOptions: Observable<string[]>;
@ -44,23 +41,19 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
initialParseConfig: { delimiter?: string; encoding?: string } = {};
constructor(
private readonly _appStateService: AppStateService,
private readonly _fileAttributesControllerService: FileAttributesControllerService,
private readonly _translateService: TranslateService,
private readonly _toaster: Toaster,
protected readonly _injector: Injector,
private readonly _formBuilder: FormBuilder,
readonly dialogRef: MatDialogRef<FileAttributesCsvImportDialogComponent>,
protected readonly _injector: Injector,
private readonly _fileAttributesControllerService: FileAttributesControllerService,
@Inject(MAT_DIALOG_DATA)
readonly data: {
csv: File;
dossierTemplateId: string;
existingConfiguration: FileAttributesConfig;
readonly csv: File;
readonly dossierTemplateId: string;
readonly existingConfiguration: FileAttributesConfig;
}
) {
super(_injector);
this.csvFile = data.csv;
this.dossierTemplateId = data.dossierTemplateId;
this.baseConfigForm = this._formBuilder.group({
filenameMappingColumnHeaderName: ['', [Validators.required, this._autocompleteStringValidator()]],
@ -132,7 +125,7 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
encoding: this.baseConfigForm.get('encoding').value
};
});
reader.readAsText(this.csvFile, this.baseConfigForm.get('encoding').value);
reader.readAsText(this.data.csv, this.baseConfigForm.get('encoding').value);
}
getSample(csvColumn: string) {
@ -199,7 +192,7 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
};
try {
await this._fileAttributesControllerService.setFileAttributesConfig(fileAttributes, this.dossierTemplateId).toPromise();
await this._fileAttributesControllerService.setFileAttributesConfig(fileAttributes, this.data.dossierTemplateId).toPromise();
this._toaster.success(_('file-attributes-csv-import.save.success'), { params: { count: this.activeFields.length } });
} catch (e) {
this._toaster.error(_('file-attributes-csv-import.save.error'));

View File

@ -4,7 +4,7 @@ import { AuditControllerService, AuditResponse, AuditSearchRequest } from '@reda
import { Moment } from 'moment';
import { applyIntervalConstraints } from '@utils/date-inputs-utils';
import { LoadingService } from '@services/loading.service';
import { AutoUnsubscribeComponent } from '@iqser/common-ui';
import { AutoUnsubscribe } from '@iqser/common-ui';
import { auditCategoriesTranslations } from '../../translations/audit-categories-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { UserService } from '@services/user.service';
@ -16,7 +16,7 @@ const PAGE_SIZE = 50;
templateUrl: './audit-screen.component.html',
styleUrls: ['./audit-screen.component.scss']
})
export class AuditScreenComponent extends AutoUnsubscribeComponent implements OnDestroy {
export class AuditScreenComponent extends AutoUnsubscribe implements OnDestroy {
readonly ALL_CATEGORIES = 'allCategories';
readonly ALL_USERS = _('audit-screen.all-users');
readonly translations = auditCategoriesTranslations;

View File

@ -20,30 +20,13 @@
<redaction-admin-side-nav type="dossierTemplates"></redaction-admin-side-nav>
<div class="content-container">
<div class="header-item">
<span class="all-caps-label">
{{ 'default-colors-screen.table-header.title' | translate: { length: entitiesService.allLength$ | async } }}
</span>
</div>
<div class="table-header" redactionSyncWidth="table-item">
<iqser-table-column-name
[label]="'default-colors-screen.table-col-names.key' | translate"
[withSort]="true"
column="key"
></iqser-table-column-name>
<iqser-table-column-name
[label]="'default-colors-screen.table-col-names.color' | translate"
class="flex-center"
></iqser-table-column-name>
<div></div>
<div class="scrollbar-placeholder"></div>
</div>
<redaction-table-header
[hasEmptyColumn]="true"
[tableColumnConfigs]="tableColumnConfigs"
[tableHeaderLabel]="tableHeaderLabel"
></redaction-table-header>
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
<!-- Table lines -->
<div *cdkVirtualFor="let color of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey" class="table-item">
<div>
<div [translate]="translations[color.key]" class="table-item-title heading"></div>
@ -57,7 +40,7 @@
<div class="action-buttons">
<iqser-circle-button
(action)="openEditColorDialog($event, color)"
*ngIf="permissionsService.isAdmin()"
*ngIf="currentUser.isAdmin"
[tooltip]="'default-colors-screen.action.edit' | translate"
icon="red:edit"
[type]="circleButtonTypes.dark"

View File

@ -1,30 +1,26 @@
.content-container {
cdk-virtual-scroll-viewport {
::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: 2fr 1fr 2fr 11px;
.content-container cdk-virtual-scroll-viewport {
::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: 2fr 1fr 2fr 11px;
.table-item {
> div:not(.scrollbar-placeholder) {
padding-left: 24px;
}
.table-item {
> div:not(.scrollbar-placeholder) {
padding-left: 24px;
}
.color-wrapper {
align-items: center;
.color-wrapper {
align-items: center;
.color-square {
width: 16px;
height: 16px;
min-width: 16px;
}
.color-square {
width: 16px;
height: 16px;
min-width: 16px;
}
}
}
}
&.has-scrollbar:hover {
::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: 2fr 1fr 2fr;
}
}
&.has-scrollbar:hover ::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: 2fr 1fr 2fr;
}
}

View File

@ -2,13 +2,18 @@ import { ChangeDetectionStrategy, Component, Injector, OnInit } from '@angular/c
import { AppStateService } from '@state/app-state.service';
import { Colors, DictionaryControllerService } from '@redaction/red-ui-http';
import { ActivatedRoute } from '@angular/router';
import { PermissionsService } from '@services/permissions.service';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { LoadingService } from '@services/loading.service';
import { CircleButtonTypes } from '@iqser/common-ui';
import { BaseListingComponent, DefaultListingServices } from '@shared/base/base-listing.component';
import { CircleButtonTypes, DefaultListingServices, ListingComponent, TableColumnConfig } from '@iqser/common-ui';
import { DefaultColorType } from '@models/default-color-key.model';
import { defaultColorsTranslations } from '../../translations/default-colors-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { UserService } from '@services/user.service';
interface ListItem {
readonly key: string;
readonly value: string;
}
@Component({
templateUrl: './default-colors-screen.component.html',
@ -16,26 +21,31 @@ import { defaultColorsTranslations } from '../../translations/default-colors-tra
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [...DefaultListingServices]
})
export class DefaultColorsScreenComponent
extends BaseListingComponent<{
key: string;
value: string;
}>
implements OnInit
{
readonly circleButtonTypes = CircleButtonTypes;
readonly translations = defaultColorsTranslations;
export class DefaultColorsScreenComponent extends ListingComponent<ListItem> implements OnInit {
protected readonly _primaryKey = 'key';
readonly circleButtonTypes = CircleButtonTypes;
readonly currentUser = this._userService.currentUser;
readonly translations = defaultColorsTranslations;
readonly tableHeaderLabel = _('default-colors-screen.table-header.title');
readonly tableColumnConfigs: TableColumnConfig<ListItem>[] = [
{
label: _('default-colors-screen.table-col-names.key'),
withSort: true,
column: 'key'
},
{ label: _('default-colors-screen.table-col-names.color'), class: 'flex-center' }
];
private _colorsObj: Colors;
constructor(
private readonly _appStateService: AppStateService,
private readonly _activatedRoute: ActivatedRoute,
private readonly _dictionaryControllerService: DictionaryControllerService,
private readonly _dialogService: AdminDialogService,
protected readonly _injector: Injector,
private readonly _userService: UserService,
private readonly _loadingService: LoadingService,
readonly permissionsService: PermissionsService,
protected readonly _injector: Injector
private readonly _activatedRoute: ActivatedRoute,
private readonly _appStateService: AppStateService,
private readonly _dialogService: AdminDialogService,
private readonly _dictionaryControllerService: DictionaryControllerService
) {
super(_injector);
_appStateService.activateDossierTemplate(_activatedRoute.snapshot.params.dossierTemplateId);

View File

@ -20,72 +20,19 @@
<redaction-admin-side-nav type="dossierTemplates"></redaction-admin-side-nav>
<div class="content-container">
<div class="header-item">
<iqser-round-checkbox
(click)="toggleSelectAll()"
[active]="entitiesService.areAllSelected$ | async"
[indeterminate]="entitiesService.notAllSelected$ | async"
></iqser-round-checkbox>
<span class="all-caps-label">
{{ 'dictionary-listing.table-header.title' | translate: { length: (entitiesService.displayedLength$ | async) } }}
</span>
<iqser-circle-button
(action)="openDeleteDictionariesDialog($event)"
*ngIf="canBulkDelete$(permissionsService.isAdmin()) | async"
[tooltip]="'dictionary-listing.bulk.delete' | translate"
icon="red:trash"
[type]="circleButtonTypes.dark"
></iqser-circle-button>
<div class="attributes-actions-container">
<redaction-input-with-action
[form]="searchService.searchForm"
[placeholder]="'dictionary-listing.search' | translate"
type="search"
></redaction-input-with-action>
<div class="actions">
<iqser-icon-button
(action)="openAddEditDictionaryDialog()"
*ngIf="permissionsService.isAdmin()"
[label]="'dictionary-listing.add-new' | translate"
icon="red:plus"
[type]="iconButtonTypes.primary"
></iqser-icon-button>
</div>
</div>
</div>
<div [class.no-data]="entitiesService.noData$ | async" class="table-header" redactionSyncWidth="table-item">
<div class="select-oval-placeholder"></div>
<iqser-table-column-name
[label]="'dictionary-listing.table-col-names.type' | translate"
[withSort]="true"
column="label"
></iqser-table-column-name>
<iqser-table-column-name
[label]="'dictionary-listing.table-col-names.order-of-importance' | translate"
[withSort]="true"
class="flex-center"
column="rank"
></iqser-table-column-name>
<iqser-table-column-name
[label]="'dictionary-listing.table-col-names.hint-redaction' | translate"
class="flex-center"
></iqser-table-column-name>
<div></div>
<div class="scrollbar-placeholder"></div>
</div>
<redaction-table-header
[bulkActions]="bulkActions"
[selectionEnabled]="true"
[hasEmptyColumn]="true"
[tableColumnConfigs]="tableColumnConfigs"
[tableHeaderLabel]="tableHeaderLabel"
></redaction-table-header>
<redaction-empty-state
(action)="openAddEditDictionaryDialog()"
*ngIf="entitiesService.noData$ | async"
[buttonLabel]="'dictionary-listing.no-data.action' | translate"
[showButton]="permissionsService.isAdmin()"
[showButton]="currentUser.isAdmin"
[text]="'dictionary-listing.no-data.title' | translate"
icon="red:dictionary"
></redaction-empty-state>
@ -133,7 +80,7 @@
</div>
<div class="actions-container">
<div *ngIf="permissionsService.isAdmin()" class="action-buttons">
<div *ngIf="currentUser.isAdmin" class="action-buttons">
<iqser-circle-button
(action)="openDeleteDictionariesDialog($event, [dict])"
[tooltip]="'dictionary-listing.action.delete' | translate"
@ -167,3 +114,30 @@
</div>
</div>
</section>
<ng-template #bulkActions>
<iqser-circle-button
(action)="openDeleteDictionariesDialog($event)"
*ngIf="currentUser.isAdmin && (entitiesService.areSomeSelected$ | async)"
[tooltip]="'dictionary-listing.bulk.delete' | translate"
icon="red:trash"
[type]="circleButtonTypes.dark"
></iqser-circle-button>
<div class="attributes-actions-container">
<redaction-input-with-action
[form]="searchService.searchForm"
[placeholder]="'dictionary-listing.search' | translate"
type="search"
></redaction-input-with-action>
<div class="actions">
<iqser-icon-button
(action)="openAddEditDictionaryDialog()"
*ngIf="currentUser.isAdmin"
[label]="'dictionary-listing.add-new' | translate"
icon="red:plus"
[type]="iconButtonTypes.primary"
></iqser-icon-button>
</div>
</div>
</ng-template>

View File

@ -1,67 +1,55 @@
@import '../../../../../assets/styles/variables';
@import '../../../../../assets/styles/red-mixins';
.header-item {
padding: 0 16px 0 10px;
redaction-table-header::ng-deep .header-item {
padding-right: 16px;
}
.attributes-actions-container {
display: flex;
flex: 1;
justify-content: flex-end;
.attributes-actions-container {
display: flex;
flex: 1;
justify-content: flex-end;
> *:not(:last-child) {
margin-right: 16px;
}
> *:not(:last-child) {
margin-right: 16px;
}
}
iqser-table-column-name::ng-deep {
> div {
padding-left: 10px !important;
.content-container cdk-virtual-scroll-viewport {
::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: auto 2fr 1fr 1fr 1fr 11px;
.table-item > div:not(.scrollbar-placeholder) {
display: flex;
flex-direction: row;
padding-left: 10px;
align-items: center;
justify-content: flex-start;
&.center {
justify-content: center;
}
.color-square {
width: 16px;
height: 16px;
min-width: 16px;
margin-right: 16px;
}
.dict-name {
z-index: 1;
max-width: 100%;
}
.stats-subtitle {
margin-top: 4px;
}
}
}
}
.content-container {
cdk-virtual-scroll-viewport {
::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: auto 2fr 1fr 1fr 1fr 11px;
.table-item {
> div:not(.scrollbar-placeholder) {
display: flex;
flex-direction: row;
padding-left: 10px;
align-items: center;
justify-content: flex-start;
&.center {
justify-content: center;
}
.color-square {
width: 16px;
height: 16px;
min-width: 16px;
margin-right: 16px;
}
.dict-name {
z-index: 1;
max-width: 100%;
}
.stats-subtitle {
margin-top: 4px;
}
}
}
}
&.has-scrollbar:hover {
::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: auto 2fr 1fr 1fr 1fr;
}
}
&.has-scrollbar:hover ::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: auto 2fr 1fr 1fr 1fr;
}
}

View File

@ -4,14 +4,14 @@ import { DictionaryControllerService } from '@redaction/red-ui-http';
import { AppStateService } from '@state/app-state.service';
import { catchError, defaultIfEmpty, tap } from 'rxjs/operators';
import { forkJoin, of } from 'rxjs';
import { PermissionsService } from '@services/permissions.service';
import { ActivatedRoute } from '@angular/router';
import { TypeValueWrapper } from '@models/file/type-value.wrapper';
import { TranslateService } from '@ngx-translate/core';
import { LoadingService } from '@services/loading.service';
import { CircleButtonTypes, IconButtonTypes } from '@iqser/common-ui';
import { BaseListingComponent, DefaultListingServices } from '@shared/base/base-listing.component';
import { CircleButtonTypes, DefaultListingServices, IconButtonTypes, ListingComponent, TableColumnConfig } from '@iqser/common-ui';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { UserService } from '@services/user.service';
const toChartConfig = (dict: TypeValueWrapper): DoughnutChartConfig => ({
value: dict.entries?.length ?? 0,
@ -25,23 +25,41 @@ const toChartConfig = (dict: TypeValueWrapper): DoughnutChartConfig => ({
styleUrls: ['./dictionary-listing-screen.component.scss'],
providers: [...DefaultListingServices]
})
export class DictionaryListingScreenComponent extends BaseListingComponent<TypeValueWrapper> implements OnInit {
export class DictionaryListingScreenComponent extends ListingComponent<TypeValueWrapper> implements OnInit {
protected readonly _primaryKey = 'label';
readonly iconButtonTypes = IconButtonTypes;
readonly circleButtonTypes = CircleButtonTypes;
readonly currentUser = this._userService.currentUser;
readonly tableHeaderLabel = _('dictionary-listing.table-header.title');
readonly tableColumnConfigs: TableColumnConfig<TypeValueWrapper>[] = [
{
label: _('dictionary-listing.table-col-names.type'),
withSort: true,
column: 'label'
},
{
label: _('dictionary-listing.table-col-names.order-of-importance'),
withSort: true,
column: 'rank',
class: 'flex-center'
},
{
label: _('dictionary-listing.table-col-names.hint-redaction'),
class: 'flex-center'
}
];
chartData: DoughnutChartConfig[] = [];
protected readonly _primaryKey = 'label';
constructor(
private readonly _dialogService: AdminDialogService,
private readonly _dictionaryControllerService: DictionaryControllerService,
protected readonly _injector: Injector,
private readonly _userService: UserService,
private readonly _activatedRoute: ActivatedRoute,
private readonly _appStateService: AppStateService,
private readonly _loadingService: LoadingService,
private readonly _appStateService: AppStateService,
private readonly _dialogService: AdminDialogService,
private readonly _translateService: TranslateService,
readonly permissionsService: PermissionsService,
protected readonly _injector: Injector
private readonly _dictionaryControllerService: DictionaryControllerService
) {
super(_injector);
_loadingService.start();

View File

@ -3,7 +3,7 @@ import { DigitalSignature, DigitalSignatureControllerService } from '@redaction/
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Toaster } from '@services/toaster.service';
import { lastIndexOfEnd } from '@utils/functions';
import { AutoUnsubscribeComponent, IconButtonTypes } from '@iqser/common-ui';
import { AutoUnsubscribe, IconButtonTypes } from '@iqser/common-ui';
import { LoadingService } from '@services/loading.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { UserService } from '@services/user.service';
@ -13,7 +13,7 @@ import { UserService } from '@services/user.service';
templateUrl: './digital-signature-screen.component.html',
styleUrls: ['./digital-signature-screen.component.scss']
})
export class DigitalSignatureScreenComponent extends AutoUnsubscribeComponent implements OnDestroy {
export class DigitalSignatureScreenComponent extends AutoUnsubscribe implements OnDestroy {
readonly iconButtonTypes = IconButtonTypes;
readonly currentUser = this._userService.currentUser;

View File

@ -20,72 +20,19 @@
<redaction-admin-side-nav type="dossierTemplates"></redaction-admin-side-nav>
<div class="content-container">
<div *ngIf="(entitiesService.noData$ | async) === false" class="header-item">
<iqser-round-checkbox
(click)="toggleSelectAll()"
[active]="entitiesService.areAllSelected$ | async"
[indeterminate]="entitiesService.notAllSelected$ | async"
></iqser-round-checkbox>
<span class="all-caps-label">
{{
'dossier-attributes-listing.table-header.title' | translate: { length: (entitiesService.displayedLength$ | async) }
}}
</span>
<iqser-circle-button
(action)="openConfirmDeleteAttributeDialog($event)"
*ngIf="permissionsService.isAdmin() && entitiesService.areSomeSelected$ | async"
[tooltip]="'dossier-attributes-listing.bulk.delete' | translate"
icon="red:trash"
[type]="circleButtonTypes.dark"
></iqser-circle-button>
<div class="attributes-actions-container">
<redaction-input-with-action
[form]="searchService.searchForm"
[placeholder]="'dossier-attributes-listing.search' | translate"
type="search"
></redaction-input-with-action>
<iqser-icon-button
(action)="openAddEditAttributeDialog($event)"
*ngIf="permissionsService.isAdmin()"
[label]="'dossier-attributes-listing.add-new' | translate"
icon="red:plus"
[type]="iconButtonTypes.primary"
></iqser-icon-button>
</div>
</div>
<div *ngIf="(entitiesService.noData$ | async) === false" class="table-header" redactionSyncWidth="table-item">
<div class="select-oval-placeholder"></div>
<iqser-table-column-name
[label]="'dossier-attributes-listing.table-col-names.label' | translate"
[withSort]="true"
column="label"
></iqser-table-column-name>
<iqser-table-column-name
[label]="'dossier-attributes-listing.table-col-names.placeholder' | translate"
></iqser-table-column-name>
<iqser-table-column-name
[label]="'dossier-attributes-listing.table-col-names.type' | translate"
[withSort]="true"
column="type"
></iqser-table-column-name>
<div></div>
<div class="scrollbar-placeholder"></div>
</div>
<redaction-table-header
[bulkActions]="bulkActions"
[selectionEnabled]="true"
[hasEmptyColumn]="true"
[tableColumnConfigs]="tableColumnConfigs"
[tableHeaderLabel]="tableHeaderLabel"
></redaction-table-header>
<redaction-empty-state
(action)="openAddEditAttributeDialog($event)"
*ngIf="entitiesService.noData$ | async"
[buttonLabel]="'dossier-attributes-listing.no-data.action' | translate"
[showButton]="permissionsService.isAdmin()"
[showButton]="currentUser.isAdmin"
[text]="'dossier-attributes-listing.no-data.title' | translate"
icon="red:attribute"
></redaction-empty-state>
@ -116,7 +63,7 @@
{{ translations[attribute.type] | translate }}
</div>
<div class="actions-container">
<div *ngIf="permissionsService.isAdmin()" class="action-buttons">
<div *ngIf="currentUser.isAdmin" class="action-buttons">
<iqser-circle-button
(action)="openAddEditAttributeDialog($event, attribute)"
[tooltip]="'dossier-attributes-listing.action.edit' | translate"
@ -138,3 +85,29 @@
</div>
</div>
</section>
<ng-template #bulkActions>
<iqser-circle-button
(action)="openConfirmDeleteAttributeDialog($event)"
*ngIf="currentUser.isAdmin && entitiesService.areSomeSelected$ | async"
[tooltip]="'dossier-attributes-listing.bulk.delete' | translate"
icon="red:trash"
[type]="circleButtonTypes.dark"
></iqser-circle-button>
<div class="attributes-actions-container">
<redaction-input-with-action
[form]="searchService.searchForm"
[placeholder]="'dossier-attributes-listing.search' | translate"
type="search"
></redaction-input-with-action>
<iqser-icon-button
(action)="openAddEditAttributeDialog($event)"
*ngIf="currentUser.isAdmin"
[label]="'dossier-attributes-listing.add-new' | translate"
icon="red:plus"
[type]="iconButtonTypes.primary"
></iqser-icon-button>
</div>
</ng-template>

View File

@ -1,25 +1,40 @@
import { Component, Injector, OnInit } from '@angular/core';
import { BaseListingComponent, DefaultListingServices } from '@shared/base/base-listing.component';
import { CircleButtonTypes, DefaultListingServices, IconButtonTypes, ListingComponent, TableColumnConfig } from '@iqser/common-ui';
import { DossierAttributeConfig } from '@redaction/red-ui-http';
import { AppStateService } from '@state/app-state.service';
import { ActivatedRoute } from '@angular/router';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { LoadingService } from '@services/loading.service';
import { CircleButtonTypes, IconButtonTypes } from '@iqser/common-ui';
import { PermissionsService } from '@services/permissions.service';
import { DossierAttributesService } from '@shared/services/controller-wrappers/dossier-attributes.service';
import { dossierAttributeTypesTranslations } from '../../translations/dossier-attribute-types-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { UserService } from '@services/user.service';
@Component({
templateUrl: './dossier-attributes-listing-screen.component.html',
styleUrls: ['./dossier-attributes-listing-screen.component.scss'],
providers: [...DefaultListingServices]
})
export class DossierAttributesListingScreenComponent extends BaseListingComponent<DossierAttributeConfig> implements OnInit {
export class DossierAttributesListingScreenComponent extends ListingComponent<DossierAttributeConfig> implements OnInit {
protected readonly _primaryKey = 'label';
readonly iconButtonTypes = IconButtonTypes;
readonly circleButtonTypes = CircleButtonTypes;
readonly currentUser = this._userService.currentUser;
readonly translations = dossierAttributeTypesTranslations;
protected readonly _primaryKey = 'label';
readonly tableHeaderLabel = _('dossier-attributes-listing.table-header.title');
readonly tableColumnConfigs: TableColumnConfig<DossierAttributeConfig>[] = [
{
label: _('dossier-attributes-listing.table-col-names.label'),
withSort: true,
column: 'label'
},
{ label: _('dossier-attributes-listing.table-col-names.placeholder') },
{
label: _('dossier-attributes-listing.table-col-names.type'),
withSort: true,
column: 'type'
}
];
constructor(
protected readonly _injector: Injector,
@ -28,7 +43,7 @@ export class DossierAttributesListingScreenComponent extends BaseListingComponen
private readonly _dialogService: AdminDialogService,
private readonly _loadingService: LoadingService,
private readonly _dossierAttributesService: DossierAttributesService,
readonly permissionsService: PermissionsService
private readonly _userService: UserService
) {
super(_injector);
_appStateService.activateDossierTemplate(_activatedRoute.snapshot.params.dossierTemplateId);

View File

@ -94,7 +94,7 @@
<ng-template #bulkActions>
<iqser-circle-button
(action)="openBulkDeleteTemplatesDialog($event)"
*ngIf="canBulkDelete$(currentUser.isAdmin) | async"
*ngIf="currentUser.isAdmin && (entitiesService.areSomeSelected$ | async)"
[tooltip]="'dossier-templates-listing.bulk.delete' | translate"
[type]="circleButtonTypes.dark"
icon="red:trash"

View File

@ -5,8 +5,7 @@ import { AdminDialogService } from '../../services/admin-dialog.service';
import { DossierTemplateModelWrapper } from '@models/file/dossier-template-model.wrapper';
import { LoadingService } from '@services/loading.service';
import { DossierTemplateControllerService } from '@redaction/red-ui-http';
import { CircleButtonTypes, IconButtonTypes, TableColumnConfig } from '@iqser/common-ui';
import { BaseListingComponent, DefaultListingServices } from '@shared/base/base-listing.component';
import { CircleButtonTypes, DefaultListingServices, IconButtonTypes, ListingComponent, TableColumnConfig } from '@iqser/common-ui';
import { UserService } from '@services/user.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
@ -16,7 +15,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [...DefaultListingServices]
})
export class DossierTemplatesListingScreenComponent extends BaseListingComponent<DossierTemplateModelWrapper> implements OnInit {
export class DossierTemplatesListingScreenComponent extends ListingComponent<DossierTemplateModelWrapper> implements OnInit {
protected readonly _primaryKey = 'name';
readonly iconButtonTypes = IconButtonTypes;
readonly circleButtonTypes = CircleButtonTypes;

View File

@ -20,89 +20,13 @@
<redaction-admin-side-nav type="dossierTemplates"></redaction-admin-side-nav>
<div class="content-container">
<div class="header-item">
<iqser-round-checkbox
(click)="toggleSelectAll()"
[active]="entitiesService.areAllSelected$ | async"
[indeterminate]="entitiesService.notAllSelected$ | async"
></iqser-round-checkbox>
<span class="all-caps-label">
{{ 'file-attributes-listing.table-header.title' | translate: { length: (entitiesService.displayedLength$ | async) } }}
</span>
<iqser-circle-button
(click)="openConfirmDeleteAttributeDialog($event)"
*ngIf="canBulkDelete$(permissionsService.isAdmin()) | async"
[tooltip]="'file-attributes-listing.bulk-actions.delete' | translate"
icon="red:trash"
[type]="circleButtonTypes.dark"
></iqser-circle-button>
<div class="attributes-actions-container">
<redaction-input-with-action
[form]="searchService.searchForm"
[placeholder]="'file-attributes-listing.search' | translate"
type="search"
></redaction-input-with-action>
<input #fileInput (change)="importCSV($event.target['files'])" accept=".csv" class="csv-input" type="file" />
<iqser-circle-button
(action)="fileInput.click()"
*ngIf="permissionsService.isAdmin()"
[tooltip]="'file-attributes-listing.upload-csv' | translate"
icon="red:upload"
tooltipPosition="above"
[type]="circleButtonTypes.dark"
></iqser-circle-button>
<iqser-icon-button
(action)="openAddEditAttributeDialog($event)"
*ngIf="permissionsService.isAdmin()"
[label]="'file-attributes-listing.add-new' | translate"
icon="red:plus"
[type]="iconButtonTypes.primary"
></iqser-icon-button>
</div>
</div>
<div [class.no-data]="entitiesService.noData$ | async" class="table-header" redactionSyncWidth="table-item">
<div class="select-oval-placeholder"></div>
<iqser-table-column-name
[label]="'file-attributes-listing.table-col-names.name' | translate"
[withSort]="true"
column="label"
></iqser-table-column-name>
<iqser-table-column-name
[label]="'file-attributes-listing.table-col-names.type' | translate"
[withSort]="true"
column="type"
></iqser-table-column-name>
<iqser-table-column-name
[label]="'file-attributes-listing.table-col-names.read-only' | translate"
[withSort]="true"
class="flex-center"
column="editable"
></iqser-table-column-name>
<iqser-table-column-name
[label]="'file-attributes-listing.table-col-names.csv-column' | translate"
></iqser-table-column-name>
<iqser-table-column-name
[label]="'file-attributes-listing.table-col-names.primary' | translate"
[rightIconTooltip]="'file-attributes-listing.table-col-names.primary-info-tooltip' | translate"
class="flex-center"
rightIcon="red:status-info"
></iqser-table-column-name>
<div></div>
<div class="scrollbar-placeholder"></div>
</div>
<redaction-table-header
[bulkActions]="bulkActions"
[selectionEnabled]="true"
[hasEmptyColumn]="true"
[tableColumnConfigs]="tableColumnConfigs"
[tableHeaderLabel]="tableHeaderLabel"
></redaction-table-header>
<redaction-empty-state
*ngIf="entitiesService.noData$ | async"
@ -116,7 +40,6 @@
></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
<!-- Table lines -->
<div *cdkVirtualFor="let attribute of sortedDisplayedEntities$ | async" class="table-item">
<div (click)="toggleEntitySelected($event, attribute)" class="selection-column">
<iqser-round-checkbox [active]="isSelected(attribute)"></iqser-round-checkbox>
@ -143,7 +66,7 @@
<iqser-round-checkbox *ngIf="attribute.primaryAttribute" [active]="true" [size]="18"></iqser-round-checkbox>
</div>
<div class="actions-container">
<div *ngIf="permissionsService.isAdmin()" class="action-buttons">
<div *ngIf="currentUser.isAdmin" class="action-buttons">
<iqser-circle-button
(action)="openAddEditAttributeDialog($event, attribute)"
[tooltip]="'file-attributes-listing.action.edit' | translate"
@ -166,3 +89,40 @@
<div class="right-container"></div>
</div>
</section>
<ng-template #bulkActions>
<iqser-circle-button
(click)="openConfirmDeleteAttributeDialog($event)"
*ngIf="currentUser.isAdmin && (entitiesService.areSomeSelected$ | async)"
[tooltip]="'file-attributes-listing.bulk-actions.delete' | translate"
icon="red:trash"
[type]="circleButtonTypes.dark"
></iqser-circle-button>
<div class="attributes-actions-container">
<redaction-input-with-action
[form]="searchService.searchForm"
[placeholder]="'file-attributes-listing.search' | translate"
type="search"
></redaction-input-with-action>
<input #fileInput (change)="importCSV($event.target['files'])" accept=".csv" class="csv-input" type="file" />
<iqser-circle-button
(action)="fileInput.click()"
*ngIf="currentUser.isAdmin"
[tooltip]="'file-attributes-listing.upload-csv' | translate"
icon="red:upload"
tooltipPosition="above"
[type]="circleButtonTypes.dark"
></iqser-circle-button>
<iqser-icon-button
(action)="openAddEditAttributeDialog($event)"
*ngIf="currentUser.isAdmin"
[label]="'file-attributes-listing.add-new' | translate"
icon="red:plus"
[type]="iconButtonTypes.primary"
></iqser-icon-button>
</div>
</ng-template>

View File

@ -5,60 +5,48 @@
justify-content: flex-end;
}
.header-item {
redaction-table-header::ng-deep .header-item {
padding: 0 24px 0 10px;
}
iqser-table-column-name::ng-deep {
> div {
padding-left: 10px !important;
.content-container cdk-virtual-scroll-viewport {
::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: auto 2fr 1fr 1fr 1fr 1fr 1fr 11px;
.table-item {
> div:not(.scrollbar-placeholder) {
padding-left: 10px;
}
> div {
&.center {
align-items: center;
}
&.label span {
@include line-clamp(1);
}
&.read-only mat-icon {
width: 14px;
height: 34px;
}
}
}
}
&.has-scrollbar:hover ::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: auto 2fr 1fr 1fr 1fr 1fr 1fr;
}
}
.content-container {
.header-item {
.attributes-actions-container {
display: flex;
flex: 1;
justify-content: flex-end;
.attributes-actions-container {
display: flex;
flex: 1;
justify-content: flex-end;
> *:not(:last-child) {
margin-right: 10px;
}
}
}
cdk-virtual-scroll-viewport {
::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: auto 2fr 1fr 1fr 1fr 1fr 1fr 11px;
.table-item {
> div:not(.scrollbar-placeholder) {
padding-left: 10px;
}
> div {
&.center {
align-items: center;
}
&.label span {
@include line-clamp(1);
}
&.read-only mat-icon {
width: 14px;
height: 34px;
}
}
}
}
&.has-scrollbar:hover {
::ng-deep.cdk-virtual-scroll-content-wrapper {
grid-template-columns: auto 2fr 1fr 1fr 1fr 1fr 1fr;
}
}
> *:not(:last-child) {
margin-right: 10px;
}
}

View File

@ -1,13 +1,13 @@
import { ChangeDetectionStrategy, Component, ElementRef, Injector, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { PermissionsService } from '@services/permissions.service';
import { FileAttributeConfig, FileAttributesConfig, FileAttributesControllerService } from '@redaction/red-ui-http';
import { AppStateService } from '@state/app-state.service';
import { ActivatedRoute } from '@angular/router';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { LoadingService } from '@services/loading.service';
import { CircleButtonTypes, IconButtonTypes } from '@iqser/common-ui';
import { BaseListingComponent, DefaultListingServices } from '@shared/base/base-listing.component';
import { CircleButtonTypes, DefaultListingServices, IconButtonTypes, ListingComponent, TableColumnConfig } from '@iqser/common-ui';
import { fileAttributeTypesTranslations } from '../../translations/file-attribute-types-translations';
import { UserService } from '@services/user.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
@Component({
templateUrl: './file-attributes-listing-screen.component.html',
@ -15,22 +15,50 @@ import { fileAttributeTypesTranslations } from '../../translations/file-attribut
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [...DefaultListingServices]
})
export class FileAttributesListingScreenComponent extends BaseListingComponent<FileAttributeConfig> implements OnInit, OnDestroy {
export class FileAttributesListingScreenComponent extends ListingComponent<FileAttributeConfig> implements OnInit, OnDestroy {
protected readonly _primaryKey = 'label';
readonly iconButtonTypes = IconButtonTypes;
readonly circleButtonTypes = CircleButtonTypes;
readonly currentUser = this._userService.currentUser;
readonly translations = fileAttributeTypesTranslations;
protected readonly _primaryKey = 'label';
readonly tableHeaderLabel = _('file-attributes-listing.table-header.title');
readonly tableColumnConfigs: TableColumnConfig<FileAttributeConfig>[] = [
{
label: _('file-attributes-listing.table-col-names.name'),
withSort: true,
column: 'label'
},
{
label: _('file-attributes-listing.table-col-names.type'),
withSort: true,
column: 'type'
},
{
label: _('file-attributes-listing.table-col-names.read-only'),
withSort: true,
column: 'editable',
class: 'flex-center'
},
{ label: _('file-attributes-listing.table-col-names.csv-column') },
{
label: _('file-attributes-listing.table-col-names.primary'),
class: 'flex-center',
rightIcon: 'red:status-info',
rightIconTooltip: _('file-attributes-listing.table-col-names.primary-info-tooltip')
}
];
private _existingConfiguration: FileAttributesConfig;
@ViewChild('fileInput') private _fileInput: ElementRef;
constructor(
readonly permissionsService: PermissionsService,
private readonly _fileAttributesService: FileAttributesControllerService,
private readonly _appStateService: AppStateService,
protected readonly _injector: Injector,
private readonly _userService: UserService,
private readonly _activatedRoute: ActivatedRoute,
private readonly _dialogService: AdminDialogService,
private readonly _loadingService: LoadingService,
protected readonly _injector: Injector
private readonly _appStateService: AppStateService,
private readonly _dialogService: AdminDialogService,
private readonly _fileAttributesService: FileAttributesControllerService
) {
super(_injector);
_appStateService.activateDossierTemplate(_activatedRoute.snapshot.params.dossierTemplateId);

View File

@ -8,7 +8,7 @@ import {
SMTPConfigurationModel
} from '@redaction/red-ui-http';
import { AppConfigService } from '@app-config/app-config.service';
import { AutoUnsubscribeComponent, IconButtonTypes } from '@iqser/common-ui';
import { AutoUnsubscribe, IconButtonTypes } from '@iqser/common-ui';
import { Toaster } from '@services/toaster.service';
import { LoadingService } from '@services/loading.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
@ -19,7 +19,7 @@ import { UserService } from '@services/user.service';
templateUrl: './general-config-screen.component.html',
styleUrls: ['./general-config-screen.component.scss']
})
export class GeneralConfigScreenComponent extends AutoUnsubscribeComponent implements OnInit, OnDestroy {
export class GeneralConfigScreenComponent extends AutoUnsubscribe implements OnInit, OnDestroy {
readonly iconButtonTypes = IconButtonTypes;
readonly currentUser = this._userService.currentUser;

View File

@ -4,8 +4,7 @@ import { Dossier } from '@redaction/red-ui-http';
import { LoadingService } from '@services/loading.service';
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
import * as moment from 'moment';
import { CircleButtonTypes, TableColumnConfig } from '@iqser/common-ui';
import { BaseListingComponent, DefaultListingServices } from '@shared/base/base-listing.component';
import { CircleButtonTypes, DefaultListingServices, ListingComponent, TableColumnConfig } from '@iqser/common-ui';
import { DossiersService } from '../../../dossier/services/dossiers.service';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { ConfirmationDialogInput, TitleColors } from '@shared/dialogs/confirmation-dialog/confirmation-dialog.component';
@ -25,7 +24,7 @@ interface DossierListItem extends Dossier {
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [...DefaultListingServices, DossiersService]
})
export class TrashScreenComponent extends BaseListingComponent<DossierListItem> implements OnInit {
export class TrashScreenComponent extends ListingComponent<DossierListItem> implements OnInit {
readonly itemSize = 80;
readonly circleButtonTypes = CircleButtonTypes;
readonly tableHeaderLabel = _('trash.table-header.title');

View File

@ -33,49 +33,13 @@
<div class="red-content-inner">
<div [class.extended]="collapsedDetails" class="content-container">
<div class="header-item">
<iqser-round-checkbox
(click)="toggleSelectAll()"
[active]="entitiesService.areAllSelected$ | async"
[indeterminate]="entitiesService.notAllSelected$ | async"
></iqser-round-checkbox>
<span class="all-caps-label">
{{ 'user-listing.table-header.title' | translate: { length: (entitiesService.displayedLength$ | async) } }}
</span>
<iqser-circle-button
(action)="bulkDelete()"
*ngIf="entitiesService.areSomeSelected$ | async"
[disabled]="(canDeleteSelected$ | async) === false"
[tooltip]="
(canDeleteSelected$ | async)
? ('user-listing.bulk.delete' | translate)
: ('user-listing.bulk.delete-disabled' | translate)
"
icon="red:trash"
tooltipPosition="after"
[type]="circleButtonTypes.dark"
></iqser-circle-button>
</div>
<div class="table-header" redactionSyncWidth="table-item">
<div class="select-oval-placeholder"></div>
<iqser-table-column-name [label]="'user-listing.table-col-names.name' | translate"></iqser-table-column-name>
<iqser-table-column-name [label]="'user-listing.table-col-names.email' | translate"></iqser-table-column-name>
<iqser-table-column-name
[label]="'user-listing.table-col-names.active' | translate"
class="flex-center"
></iqser-table-column-name>
<iqser-table-column-name [label]="'user-listing.table-col-names.roles' | translate"></iqser-table-column-name>
<div></div>
<div class="scrollbar-placeholder"></div>
</div>
<redaction-table-header
[bulkActions]="bulkActions"
[selectionEnabled]="true"
[hasEmptyColumn]="true"
[tableColumnConfigs]="tableColumnConfigs"
[tableHeaderLabel]="tableHeaderLabel"
></redaction-table-header>
<redaction-empty-state *ngIf="noMatch$ | async" [text]="'user-listing.no-match.title' | translate"></redaction-empty-state>
@ -129,3 +93,17 @@
</div>
</div>
</section>
<ng-template #bulkActions>
<iqser-circle-button
(action)="bulkDelete()"
*ngIf="entitiesService.areSomeSelected$ | async"
[disabled]="(canDeleteSelected$ | async) === false"
[tooltip]="
(canDeleteSelected$ | async) ? ('user-listing.bulk.delete' | translate) : ('user-listing.bulk.delete-disabled' | translate)
"
icon="red:trash"
tooltipPosition="after"
[type]="circleButtonTypes.dark"
></iqser-circle-button>
</ng-template>

View File

@ -7,24 +7,31 @@ import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/si
import { TranslateChartService } from '@services/translate-chart.service';
import { LoadingService } from '@services/loading.service';
import { InitialsAvatarComponent } from '@shared/components/initials-avatar/initials-avatar.component';
import { CircleButtonTypes, IconButtonTypes } from '@iqser/common-ui';
import { BaseListingComponent, DefaultListingServices } from '@shared/base/base-listing.component';
import { CircleButtonTypes, DefaultListingServices, IconButtonTypes, ListingComponent, TableColumnConfig } from '@iqser/common-ui';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { rolesTranslations } from '../../../../translations/roles-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
@Component({
templateUrl: './user-listing-screen.component.html',
styleUrls: ['./user-listing-screen.component.scss'],
providers: [...DefaultListingServices]
})
export class UserListingScreenComponent extends BaseListingComponent<UserWrapper> implements OnInit {
export class UserListingScreenComponent extends ListingComponent<UserWrapper> implements OnInit {
protected readonly _primaryKey = 'id';
readonly translations = rolesTranslations;
readonly iconButtonTypes = IconButtonTypes;
readonly circleButtonTypes = CircleButtonTypes;
readonly currentUser = this.userService.currentUser;
readonly canDeleteSelected$ = this._canDeleteSelected$;
readonly tableHeaderLabel = _('user-listing.table-header.title');
readonly tableColumnConfigs: TableColumnConfig<UserWrapper>[] = [
{ label: _('user-listing.table-col-names.name') },
{ label: _('user-listing.table-col-names.email') },
{ label: _('user-listing.table-col-names.active'), class: 'flex-center' },
{ label: _('user-listing.table-col-names.roles') }
];
collapsedDetails = false;
chartData: DoughnutChartConfig[] = [];
@ -43,7 +50,7 @@ export class UserListingScreenComponent extends BaseListingComponent<UserWrapper
super(_injector);
}
get _canDeleteSelected$(): Observable<boolean> {
private get _canDeleteSelected$(): Observable<boolean> {
const entities$ = this.entitiesService.selected$;
return entities$.pipe(map(all => all.indexOf(this.currentUser) === -1));
}

View File

@ -16,8 +16,7 @@ import { DossiersDialogService } from '../../services/dossiers-dialog.service';
import { OnAttach, OnDetach } from '@utils/custom-route-reuse.strategy';
import { UserPreferenceService } from '@services/user-preference.service';
import { ButtonConfig } from '@shared/components/page-header/models/button-config.model';
import { NestedFilter, TableColumnConfig } from '@iqser/common-ui';
import { BaseListingComponent, DefaultListingServices } from '@shared/base/base-listing.component';
import { DefaultListingServices, ListingComponent, NestedFilter, TableColumnConfig } from '@iqser/common-ui';
import { workloadTranslations } from '../../translations/workload-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { fileStatusTranslations } from '../../translations/file-status-translations';
@ -38,7 +37,7 @@ const isLeavingScreen = event => event instanceof NavigationStart && event.url !
providers: [...DefaultListingServices]
})
export class DossierListingScreenComponent
extends BaseListingComponent<DossierWrapper>
extends ListingComponent<DossierWrapper>
implements OnInit, AfterViewInit, OnDestroy, OnAttach, OnDetach
{
readonly itemSize = 85;

View File

@ -21,8 +21,7 @@ import { DossierWrapper } from '@state/model/dossier.wrapper';
import { OnAttach, OnDetach } from '@utils/custom-route-reuse.strategy';
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
import { ActionConfig } from '@shared/components/page-header/models/action-config.model';
import { CircleButtonTypes, keyChecker, NestedFilter, TableColumnConfig } from '@iqser/common-ui';
import { BaseListingComponent, DefaultListingServices } from '@shared/base/base-listing.component';
import { CircleButtonTypes, DefaultListingServices, keyChecker, ListingComponent, NestedFilter, TableColumnConfig } from '@iqser/common-ui';
import { LoadingService } from '@services/loading.service';
import { DossierAttributesService } from '@shared/services/controller-wrappers/dossier-attributes.service';
import { DossierAttributeWithValue } from '@models/dossier-attributes.model';
@ -39,10 +38,7 @@ import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
styleUrls: ['./dossier-overview-screen.component.scss'],
providers: [...DefaultListingServices]
})
export class DossierOverviewScreenComponent
extends BaseListingComponent<FileStatusWrapper>
implements OnInit, OnDestroy, OnDetach, OnAttach
{
export class DossierOverviewScreenComponent extends ListingComponent<FileStatusWrapper> implements OnInit, OnDestroy, OnDetach, OnAttach {
readonly itemSize = 80;
readonly circleButtonTypes = CircleButtonTypes;
readonly currentUser = this._userService.currentUser;

View File

@ -33,7 +33,7 @@ import { OnAttach, OnDetach } from '@utils/custom-route-reuse.strategy';
import { LoadingService } from '@services/loading.service';
import { stampPDFPage } from '@utils/page-stamper';
import { TranslateService } from '@ngx-translate/core';
import { AutoUnsubscribeComponent, CircleButtonTypes, NestedFilter, processFilters } from '@iqser/common-ui';
import { AutoUnsubscribe, CircleButtonTypes, NestedFilter, processFilters } from '@iqser/common-ui';
import { fileStatusTranslations } from '../../translations/file-status-translations';
import { handleFilterDelta } from '@shared/components/filters/popup-filter/utils/filter-utils';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
@ -45,7 +45,7 @@ const ALL_HOTKEY_ARRAY = ['Escape', 'F', 'f'];
templateUrl: './file-preview-screen.component.html',
styleUrls: ['./file-preview-screen.component.scss']
})
export class FilePreviewScreenComponent extends AutoUnsubscribeComponent implements OnInit, OnDestroy, OnAttach, OnDetach {
export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnInit, OnDestroy, OnAttach, OnDetach {
readonly circleButtonTypes = CircleButtonTypes;
readonly translations = fileStatusTranslations;

View File

@ -1,10 +1,9 @@
import { Component, Injector, OnDestroy } from '@angular/core';
import { BaseListingComponent, DefaultListingServices } from '@shared/base/base-listing.component';
import { DefaultListingServices, keyChecker, ListingComponent, TableColumnConfig } from '@iqser/common-ui';
import { MatchedDocument, SearchControllerService, SearchResult } from '@redaction/red-ui-http';
import { BehaviorSubject, Observable } from 'rxjs';
import { debounceTime, map, skip, switchMap, tap } from 'rxjs/operators';
import { ActivatedRoute, Router } from '@angular/router';
import { keyChecker, TableColumnConfig } from '@iqser/common-ui';
import { AppStateService } from '@state/app-state.service';
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
import { LoadingService } from '@services/loading.service';
@ -35,7 +34,7 @@ interface SearchInput {
styleUrls: ['./search-screen.component.scss'],
providers: [...DefaultListingServices]
})
export class SearchScreenComponent extends BaseListingComponent<ListItem> implements OnDestroy {
export class SearchScreenComponent extends ListingComponent<ListItem> implements OnDestroy {
readonly fileStatusTranslations = fileStatusTranslations;
readonly searchPositions = SearchPositions;

View File

@ -1,90 +0,0 @@
import { Directive, Injector, OnDestroy } from '@angular/core';
import {
AutoUnsubscribeComponent,
Bind,
EntitiesService,
FilterService,
KeysOf,
SearchService,
SortingOrders,
SortingService,
TableColumnConfig
} from '@iqser/common-ui';
import { combineLatest, Observable } from 'rxjs';
import { distinctUntilChanged, map, switchMap } from 'rxjs/operators';
export const DefaultListingServices = [FilterService, SearchService, EntitiesService, SortingService] as const;
@Directive()
export abstract class BaseListingComponent<T extends object> extends AutoUnsubscribeComponent implements OnDestroy {
readonly filterService = this._injector.get(FilterService);
readonly searchService = this._injector.get<SearchService<T>>(SearchService);
readonly sortingService = this._injector.get<SortingService<T>>(SortingService);
readonly entitiesService = this._injector.get<EntitiesService<T>>(EntitiesService);
readonly noMatch$ = this._noMatch$;
readonly sortedDisplayedEntities$ = this._sortedDisplayedEntities$;
readonly tableColumnConfigs: TableColumnConfig<T>[];
/**
* Key used in the *trackBy* function with **ngFor* or **cdkVirtualFor*
* and in the default sorting and as the search field
* @protected
*/
protected readonly _primaryKey: KeysOf<T>;
protected constructor(protected readonly _injector: Injector) {
super();
setTimeout(() => this.setInitialConfig());
}
get allEntities(): T[] {
return this.entitiesService.all;
}
private get _sortedDisplayedEntities$(): Observable<T[]> {
const sort = entities => this.sortingService.defaultSort(entities);
const sortedEntities = () => this.entitiesService.displayed$.pipe(map(sort));
return this.sortingService.sortingOption$.pipe(switchMap(sortedEntities));
}
private get _noMatch$(): Observable<boolean> {
return combineLatest([this.entitiesService.allLength$, this.entitiesService.displayedLength$]).pipe(
map(([hasEntities, hasDisplayedEntities]) => hasEntities && !hasDisplayedEntities),
distinctUntilChanged()
);
}
setInitialConfig() {
this.sortingService.setSortingOption({
column: this._primaryKey,
order: SortingOrders.asc
});
this.searchService.setSearchKey(this._primaryKey);
}
canBulkDelete$(hasPermission = true): Observable<boolean> {
return this.entitiesService.areSomeSelected$.pipe(
map(areSomeEntitiesSelected => areSomeEntitiesSelected && hasPermission),
distinctUntilChanged()
);
}
toggleSelectAll() {
return this.entitiesService.selectAll();
}
toggleEntitySelected(event: MouseEvent, entity: T) {
event.stopPropagation();
return this.entitiesService.select(entity);
}
isSelected(entity: T): boolean {
return this.entitiesService.isSelected(entity);
}
@Bind()
trackByPrimaryKey(index: number, item: T) {
return item[this._primaryKey];
}
}

View File

@ -4,7 +4,7 @@ import { DossierWrapper } from '@state/model/dossier.wrapper';
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
import { FileDownloadService } from '@upload-download/services/file-download.service';
import { Toaster } from '@services/toaster.service';
import { AutoUnsubscribeComponent, CircleButtonType, CircleButtonTypes } from '@iqser/common-ui';
import { AutoUnsubscribe, CircleButtonType, CircleButtonTypes } from '@iqser/common-ui';
import { TranslateService } from '@ngx-translate/core';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
@ -16,7 +16,7 @@ export type MenuState = 'OPEN' | 'CLOSED';
styleUrls: ['./file-download-btn.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class FileDownloadBtnComponent extends AutoUnsubscribeComponent implements OnDestroy {
export class FileDownloadBtnComponent extends AutoUnsubscribe implements OnDestroy {
@Input() dossier: DossierWrapper;
@Input() file: FileStatusWrapper | FileStatusWrapper[];
@Input() tooltipPosition: 'above' | 'below' | 'before' | 'after' = 'above';

View File

@ -1,7 +1,7 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy } from '@angular/core';
import { UserService, UserWrapper } from '@services/user.service';
import { TranslateService } from '@ngx-translate/core';
import { AutoUnsubscribeComponent } from '@iqser/common-ui';
import { AutoUnsubscribe } from '@iqser/common-ui';
@Component({
selector: 'redaction-initials-avatar',
@ -9,7 +9,7 @@ import { AutoUnsubscribeComponent } from '@iqser/common-ui';
styleUrls: ['./initials-avatar.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class InitialsAvatarComponent extends AutoUnsubscribeComponent implements OnChanges, OnDestroy {
export class InitialsAvatarComponent extends AutoUnsubscribe implements OnChanges, OnDestroy {
@Input() userId: string;
@Input() color = 'lightgray';
@Input() size: 'small' | 'large' = 'small';

View File

@ -12,7 +12,7 @@
<ng-container [ngTemplateOutlet]="bulkActions"></ng-container>
<redaction-quick-filters></redaction-quick-filters>
<redaction-quick-filters *ngIf="hasQuickFilters$ | async"></redaction-quick-filters>
<!-- Custom content-->
<ng-content></ng-content>

View File

@ -1,5 +1,6 @@
import { ChangeDetectionStrategy, Component, Input, TemplateRef } from '@angular/core';
import { EntitiesService, Required, TableColumnConfig } from '@iqser/common-ui';
import { EntitiesService, FilterService, Required, TableColumnConfig } from '@iqser/common-ui';
import { map } from 'rxjs/operators';
@Component({
selector: 'redaction-table-header',
@ -14,5 +15,7 @@ export class TableHeaderComponent<T extends object> {
@Input() selectionEnabled = false;
@Input() bulkActions: TemplateRef<any>;
constructor(readonly entitiesService: EntitiesService<T>) {}
readonly hasQuickFilters$ = this.filterService.getGroup$('quickFilters').pipe(map(filters => !!filters));
constructor(readonly entitiesService: EntitiesService<T>, readonly filterService: FilterService) {}
}

View File

@ -25,8 +25,7 @@ export class SyncWidthDirective implements AfterViewInit, OnDestroy {
@Debounce(10)
matchWidth() {
const headerItems = this._elementRef.nativeElement.children;
const tableRows = document.getElementsByClassName(this.redactionSyncWidth);
// const tableRows = this._elementRef.nativeElement.parentElement.getElementsByClassName(this.redactionSyncWidth);
const tableRows = this._elementRef.nativeElement.parentElement.parentElement.getElementsByClassName(this.redactionSyncWidth);
if (!tableRows || !tableRows.length) {
return;