Edit dossier attributes

This commit is contained in:
Adina Țeudan 2021-07-16 15:30:08 +03:00
parent efa4e76f62
commit 95edb70dea
16 changed files with 221 additions and 42 deletions

View File

@ -4,7 +4,7 @@ import { FileAttributeConfig } from '@redaction/red-ui-http';
import { FilterService } from '@shared/services/filter.service';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { SortingService } from '../../../../../services/sorting.service';
import { SortingService } from '@services/sorting.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
@Component({

View File

@ -11,7 +11,7 @@ import { TranslateService } from '@ngx-translate/core';
import { FilterService } from '@shared/services/filter.service';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { SortingService } from '../../../../services/sorting.service';
import { SortingService } from '@services/sorting.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
export interface Field {

View File

@ -4,8 +4,8 @@ import { DossierAttributeConfig, DossierAttributesControllerService } from '@red
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 { ScreenNames, SortingService } from '../../../../services/sorting.service';
import { LoadingService } from '@services/loading.service';
import { ScreenNames, SortingService } from '@services/sorting.service';
import { FilterService } from '@shared/services/filter.service';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';

View File

@ -3,13 +3,13 @@ import { AppStateService } from '@state/app-state.service';
import { PermissionsService } from '@services/permissions.service';
import { UserPreferenceService } from '@services/user-preference.service';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { DossierTemplateModelWrapper } from '../../../../models/file/dossier-template-model.wrapper';
import { LoadingService } from '../../../../services/loading.service';
import { DossierTemplateModelWrapper } from '@models/file/dossier-template-model.wrapper';
import { LoadingService } from '@services/loading.service';
import { DossierTemplateControllerService } from '@redaction/red-ui-http';
import { FilterService } from '@shared/services/filter.service';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { ScreenNames, SortingService } from '../../../../services/sorting.service';
import { ScreenNames, SortingService } from '@services/sorting.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
@Component({

View File

@ -4,11 +4,11 @@ import { FileAttributeConfig, FileAttributesConfig, FileAttributesControllerServ
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 { LoadingService } from '@services/loading.service';
import { FilterService } from '@shared/services/filter.service';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { ScreenNames, SortingService } from '../../../../services/sorting.service';
import { ScreenNames, SortingService } from '@services/sorting.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
@Component({

View File

@ -4,8 +4,8 @@ import { AppStateService } from '@state/app-state.service';
import { ReportTemplate, ReportTemplateControllerService } from '@redaction/red-ui-http';
import { download } from '../../../../utils/file-download-utils';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { LoadingService } from '../../../../services/loading.service';
import { PermissionsService } from '../../../../services/permissions.service';
import { LoadingService } from '@services/loading.service';
import { PermissionsService } from '@services/permissions.service';
@Component({
selector: 'redaction-reports-screen',

View File

@ -2,13 +2,13 @@ import { ChangeDetectionStrategy, Component, Injector, OnInit } from '@angular/c
import { AppStateService } from '@state/app-state.service';
import { PermissionsService } from '@services/permissions.service';
import { Dossier, StatusControllerService } from '@redaction/red-ui-http';
import { LoadingService } from '../../../../services/loading.service';
import { LoadingService } from '@services/loading.service';
import { AppConfigKey, AppConfigService } from '../../../app-config/app-config.service';
import * as moment from 'moment';
import { FilterService } from '@shared/services/filter.service';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { ScreenNames, SortingService } from '../../../../services/sorting.service';
import { ScreenNames, SortingService } from '@services/sorting.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { DossiersService } from '../../../dossier/services/dossiers.service';

View File

@ -6,12 +6,12 @@ import { AdminDialogService } from '../../services/admin-dialog.service';
import { TranslateService } from '@ngx-translate/core';
import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
import { TranslateChartService } from '@services/translate-chart.service';
import { LoadingService } from '../../../../services/loading.service';
import { LoadingService } from '@services/loading.service';
import { InitialsAvatarComponent } from '@shared/components/initials-avatar/initials-avatar.component';
import { FilterService } from '@shared/services/filter.service';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { SortingService } from '../../../../services/sorting.service';
import { SortingService } from '@services/sorting.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

View File

@ -9,7 +9,7 @@ import { UserService } from '@services/user.service';
import { User } from '@redaction/red-ui-http';
import { NotificationService } from '@services/notification.service';
import { FilterService } from '@shared/services/filter.service';
import { FileStatusWrapper } from '../../../../models/file/file-status.wrapper';
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
@Component({
selector: 'redaction-dossier-details',

View File

@ -1,21 +1,89 @@
<form *ngIf="attributesForm" [formGroup]="attributesForm">
<div>
<div class="heading">
{{ 'edit-dossier-dialog.nav-items.custom-dossier-attributes' | translate }}
<div *ngIf="customAttributes.length" class="heading">
{{ 'edit-dossier-dialog.attributes.custom-attributes' | translate }}
</div>
<div *ngFor="let attr of customAttributes" class="red-input-group w-300">
<redaction-empty-state
*ngIf="!customAttributes.length"
icon="red:attribute"
text="edit-dossier-dialog.attributes.no-custom-attributes"
></redaction-empty-state>
<div *ngFor="let attr of customAttributes" [class.datepicker-wrapper]="isDate(attr)" class="red-input-group">
<label>{{ attr.label }}</label>
<input [formControlName]="attr.id" [name]="attr.id" type="text" />
<input
*ngIf="isNumber(attr) || isText(attr)"
[formControlName]="attr.id"
[name]="attr.id"
[type]="isNumber(attr) ? 'number' : 'text'"
/>
<ng-container *ngIf="isDate(attr)">
<input [formControlName]="attr.id" [matDatepicker]="picker" placeholder="dd/mm/yy" />
<mat-datepicker-toggle [for]="picker" matSuffix>
<mat-icon matDatepickerToggleIcon svgIcon="red:calendar"></mat-icon>
</mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</ng-container>
</div>
</div>
<div class="separator"></div>
<div>
<div class="heading">
{{ 'edit-dossier-dialog.nav-items.image-attributes' | translate }}
<div class="image-attributes-container">
<div *ngIf="imageAttributes.length" class="heading">
{{ 'edit-dossier-dialog.attributes.image-attributes' | translate }}
</div>
<div *ngFor="let attr of imageAttributes" class="red-input-group w-300">
<label>{{ attr.label }}</label>
<input [formControlName]="attr.id" [name]="attr.id" type="text" />
<redaction-empty-state
*ngIf="!imageAttributes.length"
icon="red:attribute"
text="edit-dossier-dialog.attributes.no-image-attributes"
></redaction-empty-state>
<div
*ngFor="let attr of imageAttributes"
[class.displayed-preview]="currentAttrValue(attr)"
class="red-input-group image-attribute"
>
<div>
<img *ngIf="currentAttrValue(attr)" [alt]="attr.label" [src]="currentAttrValue(attr)" />
<label>{{ attr.label }}</label>
<redaction-icon-button
(action)="fileInputClick(attr)"
*ngIf="!currentAttrValue(attr)"
class="upload-button"
icon="red:upload"
text="edit-dossier-dialog.attributes.upload-image"
type="show-bg"
></redaction-icon-button>
<input
#fileInput
(change)="uploadImage($event, attr)"
[id]="attr.id"
[name]="attr.id"
accept="image/*"
class="file-upload-input"
hidden
type="file"
/>
</div>
<div *ngIf="currentAttrValue(attr)">
<redaction-circle-button
(action)="deleteAttr(attr)"
icon="red:trash"
tooltip="edit-dossier-dialog.attributes.delete-image"
type="dark-bg"
></redaction-circle-button>
<redaction-circle-button
(action)="fileInputClick(attr)"
icon="red:upload"
tooltip="edit-dossier-dialog.attributes.upload-image"
type="dark-bg"
></redaction-circle-button>
</div>
</div>
</div>
</form>

View File

@ -20,5 +20,53 @@
padding: 0;
background-color: $separator;
}
.datepicker-wrapper {
width: initial;
}
.datepicker-wrapper input {
width: initial;
margin-top: 3px;
}
.datepicker-wrapper:not(:first-child) {
margin-top: 14px;
}
.image-attribute {
&.displayed-preview {
flex-direction: row;
justify-content: space-between;
> div {
flex-direction: row;
align-items: center;
display: flex;
}
}
&:not(first-of-type) {
margin-top: 24px;
}
.upload-button {
margin-top: 6px;
width: fit-content;
}
img {
width: 50px;
height: 50px;
object-fit: cover;
object-position: center;
border: 1px solid $grey-5;
margin-right: 15px;
}
redaction-circle-button:not(:last-child) {
margin-right: 2px;
}
}
}
}

View File

@ -1,4 +1,4 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { Component, ElementRef, EventEmitter, Input, OnInit, Output, QueryList, ViewChildren } from '@angular/core';
import { EditDossierSectionInterface } from '../edit-dossier-section.interface';
import { DossierWrapper } from '@state/model/dossier.wrapper';
import { AppStateService } from '@state/app-state.service';
@ -6,6 +6,9 @@ import { PermissionsService } from '@services/permissions.service';
import { DossierAttributeConfig, DossierAttributesControllerService } from '@redaction/red-ui-http';
import { LoadingService } from '@services/loading.service';
import { FormBuilder, FormGroup } from '@angular/forms';
import * as moment from 'moment';
type DossierAttributeWithValue = DossierAttributeConfig & { value: any };
@Component({
selector: 'redaction-edit-dossier-attributes',
@ -15,11 +18,12 @@ import { FormBuilder, FormGroup } from '@angular/forms';
export class EditDossierAttributesComponent implements EditDossierSectionInterface, OnInit {
@Input() dossierWrapper: DossierWrapper;
@Output() updateDossier = new EventEmitter<any>();
customAttributes: (DossierAttributeConfig & { value: any })[] = [];
imageAttributes: (DossierAttributeConfig & { value: any })[] = [];
attributesMapping: (DossierAttributeConfig & { value: any })[] = [];
customAttributes: DossierAttributeWithValue[] = [];
imageAttributes: DossierAttributeWithValue[] = [];
attributesMapping: DossierAttributeWithValue[] = [];
attributesForm: FormGroup;
@ViewChildren('fileInput') private _fileInputs: QueryList<ElementRef>;
constructor(
private readonly _appStateService: AppStateService,
@ -31,7 +35,11 @@ export class EditDossierAttributesComponent implements EditDossierSectionInterfa
get changed() {
for (const attr of this.attributesMapping) {
if (this._parseAttrValue(attr.value) !== this._parseAttrValue(this.attributesForm.get(attr.id).value)) {
if (this.isDate(attr)) {
if (!moment(attr.value).isSame(moment(this.currentAttrValue(attr)))) {
return true;
}
} else if (this._parseAttrValue(attr.value) !== this._parseAttrValue(this.currentAttrValue(attr))) {
return true;
}
}
@ -54,21 +62,69 @@ export class EditDossierAttributesComponent implements EditDossierSectionInterfa
this._loadingService.start();
const dossierAttributeList = this.attributesMapping.map(attr => ({
dossierAttributeId: attr.id,
value: this.attributesForm.get(attr.id).value
value: this.currentAttrValue(attr)
}));
await this._dossierAttributesService.setDossierAttributes({ dossierAttributeList }, this.dossierWrapper.dossierId).toPromise();
await this._loadAttributes();
this.updateDossier.emit();
this._loadingService.stop();
}
updatedDossier($event) {
this.updateDossier.emit($event);
fileInputClick(attr: DossierAttributeWithValue) {
this._getFileInputById(attr.id).nativeElement.click();
}
isNumber(attr: DossierAttributeWithValue): boolean {
return attr.type === 'NUMBER';
}
isDate(attr: DossierAttributeWithValue): boolean {
return attr.type === 'DATE';
}
isImage(attr: DossierAttributeWithValue): boolean {
return attr.type === 'IMAGE';
}
isText(attr: DossierAttributeWithValue): boolean {
return attr.type === 'TEXT';
}
async uploadImage($event, attr: DossierAttributeWithValue) {
const toBase64 = file =>
new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
});
const image = $event.target.files[0];
const result = await toBase64(image);
this.attributesForm.patchValue({
[attr.id]: result
});
this._getFileInputById(attr.id).nativeElement.value = null;
}
revert() {
this._initForm();
}
currentAttrValue(attr: DossierAttributeWithValue): string {
return this.attributesForm.get(attr.id).value;
}
deleteAttr(attr: DossierAttributeWithValue) {
this.attributesForm.patchValue({
[attr.id]: null
});
}
private _getFileInputById(id: string): ElementRef {
return this._fileInputs.find(el => el.nativeElement.id === id);
}
private _parseAttrValue(value: any) {
return [null, undefined, ''].includes(value) ? undefined : value;
}
@ -83,8 +139,8 @@ export class EditDossierAttributesComponent implements EditDossierSectionInterfa
value: attributes.dossierAttributeList.find(attr => attr.dossierAttributeId === config.id)?.value
}));
this.customAttributes = this.attributesMapping.filter(attr => attr.type !== 'IMAGE');
this.imageAttributes = this.attributesMapping.filter(attr => attr.type === 'IMAGE');
this.customAttributes = this.attributesMapping.filter(attr => !this.isImage(attr));
this.imageAttributes = this.attributesMapping.filter(attr => this.isImage(attr));
}
private _initForm() {

View File

@ -22,13 +22,13 @@ import {
dossierStatusChecker,
dossierTemplateChecker
} from '@shared/components/filters/popup-filter/utils/filter-utils';
import { UserPreferenceService } from '../../../../services/user-preference.service';
import { UserPreferenceService } from '@services/user-preference.service';
import { ButtonConfig } from '@shared/components/page-header/models/button-config.model';
import { FilterService } from '@shared/services/filter.service';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { ScreenNames, SortingService } from '../../../../services/sorting.service';
import { ScreenNames, SortingService } from '@services/sorting.service';
@Component({
templateUrl: './dossier-listing-screen.component.html',

View File

@ -29,7 +29,7 @@ import { ActionConfig } from '@shared/components/page-header/models/action-confi
import { FilterService } from '@shared/services/filter.service';
import { SearchService } from '@shared/services/search.service';
import { ScreenStateService } from '@shared/services/screen-state.service';
import { ScreenNames, SortingService } from '../../../../services/sorting.service';
import { ScreenNames, SortingService } from '@services/sorting.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
@Component({

View File

@ -831,9 +831,15 @@
"general-info": "General Information",
"members": "Members",
"report-attributes": "Report Attributes",
"team-members": "Team Members",
"custom-dossier-attributes": "Custom Dossier Attributes",
"image-attributes": "Image Attributes"
"team-members": "Team Members"
},
"attributes": {
"custom-attributes": "Custom Dossier Attributes",
"image-attributes": "Image Attributes",
"upload-image": "Upload Image",
"delete-image": "Delete Image",
"no-custom-attributes": "There are no text attributes",
"no-image-attributes": "There are no image attributes"
},
"unsaved-changes": "You have unsaved changes. Save or revert before changing the tab."
},

View File

@ -196,6 +196,7 @@ form {
.mat-datepicker-toggle {
position: absolute;
right: 0;
bottom: 0;
color: $accent;
&.mat-datepicker-toggle-active {