diff --git a/src/frontend/app/administration/administration.module.ts b/src/frontend/app/administration/administration.module.ts index 820a9ef3a6da3edf7a8553890155532d9c5b99b9..f35bd496b960eb3315c14ce1c41498dce49d09f3 100755 --- a/src/frontend/app/administration/administration.module.ts +++ b/src/frontend/app/administration/administration.module.ts @@ -2,10 +2,6 @@ import { NgModule } from '@angular/core'; import { SharedModule } from '../app-common.module'; -//import { MenuComponent } from '../menu/menu.component'; -//import { MenuNavComponent } from '../menu/menu-nav.component'; -//import { MenuTopComponent } from '../menu/menu-top.component'; - import { AdministrationRoutingModule } from './administration-routing.module'; import { AdministrationComponent } from './home/administration.component'; @@ -63,9 +59,6 @@ import { ContactsPageAdministrationComponent } from './contact/page AdministrationRoutingModule ], declarations: [ - //MenuComponent, - //MenuNavComponent, - //MenuTopComponent, AdministrationComponent, UsersAdministrationComponent, UserAdministrationComponent, diff --git a/src/frontend/app/administration/contact/modal/contact-modal.component.html b/src/frontend/app/administration/contact/modal/contact-modal.component.html new file mode 100644 index 0000000000000000000000000000000000000000..68857ae59061ee5950ca50d9c0f1eb48528703b2 --- /dev/null +++ b/src/frontend/app/administration/contact/modal/contact-modal.component.html @@ -0,0 +1,10 @@ +<h1 mat-dialog-title> + <span style="flex: 1;"> + content + </span> + <button [title]="lang.close" mat-icon-button (click)="dialogRef.close();"> + <mat-icon class="fa fa-times"></mat-icon> + </button></h1> +<mat-dialog-content class="modal-container"> + <app-contact-form (onSubmitEvent)="dialogRef.close($event)"></app-contact-form> +</mat-dialog-content> \ No newline at end of file diff --git a/src/frontend/app/administration/contact/modal/contact-modal.component.scss b/src/frontend/app/administration/contact/modal/contact-modal.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..a3b61021e192c6a2fc9351c568f31a1a1aedf6e4 --- /dev/null +++ b/src/frontend/app/administration/contact/modal/contact-modal.component.scss @@ -0,0 +1,15 @@ +@import '../../../../css/vars.scss'; + +.mat-dialog-title { + padding: 10px; + display: flex; + align-items: center; +} +.modal-container{ + min-height: 250px; + height: auto; +} + +.modal-body{ + min-height: auto; +} diff --git a/src/frontend/app/administration/contact/modal/contact-modal.component.ts b/src/frontend/app/administration/contact/modal/contact-modal.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..edb6e9cc77fc3db4c92ae912e870031662e050fd --- /dev/null +++ b/src/frontend/app/administration/contact/modal/contact-modal.component.ts @@ -0,0 +1,20 @@ +import { Component, Inject } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { LANG } from '../../../translate.component'; +import { HttpClient } from '@angular/common/http'; + +@Component({ + templateUrl: 'contact-modal.component.html', + styleUrls: ['contact-modal.component.scss'], +}) +export class ContactModalComponent { + lang: any = LANG; + + constructor( + public http: HttpClient, + @Inject(MAT_DIALOG_DATA) public data: any, + public dialogRef: MatDialogRef<ContactModalComponent>) { + } + + ngOnInit(): void { } +} diff --git a/src/frontend/app/administration/contact/page/contacts-page-administration.component.html b/src/frontend/app/administration/contact/page/contacts-page-administration.component.html index 3def2a46acf76455c2b05f747015cb81cb0318aa..1e69337cc666b033c70e0ee9307f549d479e9bd7 100644 --- a/src/frontend/app/administration/contact/page/contacts-page-administration.component.html +++ b/src/frontend/app/administration/contact/page/contacts-page-administration.component.html @@ -28,243 +28,7 @@ </div> <div class="container" [class.fullContainer]="appService.getViewMode()"> <div class="container-content"> - <div class="loading" *ngIf="loading; else elseTemplate"> - <mat-spinner style="margin:auto;"></mat-spinner> - </div> - <ng-template #elseTemplate> - <mat-menu #menu="matMenu"> - <button mat-menu-item [matMenuTriggerFor]="mainInfo" - [disabled]="noField('mainInfo')">{{lang.denomination}}</button> - <button mat-menu-item [matMenuTriggerFor]="address" - [disabled]="noField('address')">{{lang.address}}</button> - <button mat-menu-item [matMenuTriggerFor]="complement" - [disabled]="noField('complement')">{{lang.additionals}}</button> - </mat-menu> - - <mat-menu #mainInfo="matMenu"> - <button mat-menu-item (click)="toogleAllFieldsUnit('mainInfo')" - style="text-align: center;">{{lang.addAll}}</button> - <mat-divider></mat-divider> - <ng-container *ngFor="let field of contactForm"> - <button mat-menu-item *ngIf="!field.display && field.unit === 'mainInfo'" - (click)="field.display=!field.display"> - <mat-icon *ngIf="field.filling" style="height: auto;padding-right: 10px;" [title]="lang.targetFillingField" class="fas fa-circle rate_{{fillingRate.class}}"></mat-icon> - <span>{{field.label}}</span> - </button> - </ng-container> - </mat-menu> - - <mat-menu #address="matMenu"> - <button mat-menu-item (click)="toogleAllFieldsUnit('address')" - style="text-align: center;">{{lang.addAll}}</button> - <mat-divider></mat-divider> - <ng-container *ngFor="let field of contactForm"> - <button mat-menu-item *ngIf="!field.display && field.unit === 'address'" - (click)="field.display=!field.display"> - <mat-icon *ngIf="field.filling" style="height: auto;padding-right: 10px;" [title]="lang.targetFillingField" class="fas fa-circle rate_{{fillingRate.class}}"></mat-icon> - <span>{{field.label}}</span> - </button> - </ng-container> - </mat-menu> - <mat-menu #complement="matMenu"> - <button mat-menu-item (click)="toogleAllFieldsUnit('complement')" - style="text-align: center;">{{lang.addAll}}</button> - <mat-divider></mat-divider> - <ng-container *ngFor="let field of contactForm"> - <button mat-menu-item *ngIf="!field.display && field.unit === 'complement'" - (click)="field.display=!field.display"> - <mat-icon *ngIf="field.filling" style="height: auto;padding-right: 10px;" [title]="lang.targetFillingField" class="fas fa-circle rate_{{fillingRate.class}}"></mat-icon> - <span>{{field.label}}</span> - </button> - </ng-container> - </mat-menu> - <div style="padding: 10px;"> - <div class="rate_{{fillingRate.class}}" >{{lang.contactFilledTo}} <b>{{fillingRate.value}} %</b></div> - <mat-progress-bar mode="determinate" class="fillingBar" [color]="fillingRate.class" [value]="fillingRate.value"></mat-progress-bar> - - </div> - <div [class.multipleUnits]="!isEmptyUnit('address') || !isEmptyUnit('complement')"> - <ng-container *ngFor="let unit of contactUnit"> - <div *ngIf="!isEmptyUnit(unit.id)"> - <mat-list> - <h3 mat-subheader class="unitTitle"><span style="flex:1">{{unit.label}}</span><a - *ngIf="unit.id === 'address'" (click)="addressBANMode=!addressBANMode" - style="cursor: pointer;">{{addressBANMode ? lang.switchManualAddress : lang.searchAddressDb}}</a> - </h3> - <ng-container *ngFor="let field of contactForm;let i=index"> - <mat-list-item class="contact-item" - *ngIf="(field.unit === unit.id && unit.id !== 'address') || (field.unit === unit.id && unit.id === 'address' && !addressBANMode)"> - <p mat-line class="contact-content" *ngIf="field.display"> - <ng-container *ngIf="field.type === 'string'"> - <mat-form-field> - <input matInput [formControl]="field.control" - [placeholder]="field.label" (blur)="checkCompany(field);checkFilling();" - [required]="field.required"> - <mat-hint - *ngIf="companyFound !== null && field.id === 'company'" - align="end">{{lang.contactsParameters_company}} <b>{{companyFound.company}}</b> - {{lang.found}} - ! - cliquez <a (click)="setAddress(companyFound)" - style="cursor: pointer;font-weight:bold;">{{lang.here}}</a> - {{lang.toCopyAddress}}</mat-hint> - <mat-error *ngIf="field.control.status!=='VALID' && field.control.touched"> - {{getErrorMsg(field.control.errors)}}</mat-error> - <mat-icon style="height: auto;" matSuffix [title]="lang.targetFillingField" class="fas fa-circle rate_{{fillingRate.class}}"></mat-icon> - </mat-form-field> - </ng-container> - <ng-container *ngIf="field.type === 'integer'"> - <mat-form-field> - <input type="text" matInput [formControl]="field.control" - [placeholder]="field.label" min="0" step="0.1" - [required]="field.required"> - <mat-error *ngIf="field.control.status!=='VALID' && field.control.touched"> - {{getErrorMsg(field.control.errors)}}</mat-error> - </mat-form-field> - </ng-container> - <ng-container *ngIf="field.type === 'select'"> - <plugin-select-search [label]="field.label" [showLabel]="true" - [formControlSelect]="field.control" - [placeholderLabel]="field.label" [datas]="field.values" - [class]="''"> - </plugin-select-search> - </ng-container> - <ng-container *ngIf="field.type === 'date'"> - <mat-form-field (click)="picker.open()" style="cursor:pointer;"> - <mat-label>{{field.label}} - </mat-label> - <input [formControl]="field.control" matInput - [matDatepicker]="picker" [placeholder]="field.label" - readonly style="cursor:pointer;"> - <mat-datepicker-toggle matSuffix [for]="picker" - *ngIf="!field.control.value"> - </mat-datepicker-toggle> - <mat-datepicker [touchUi]="appService.getViewMode()" #picker> - </mat-datepicker> - <button mat-button color="warn" matSuffix mat-icon-button - *ngIf="field.control.value && !field.control.disabled" - (click)="$event.stopPropagation();field.control.reset();" - [title]="lang.eraseValue"> - <mat-icon color="warn" class="fa fa-calendar-times"> - </mat-icon> - </button> - </mat-form-field> - </ng-container> - <ng-container *ngIf="field.type === 'radio'"> - <mat-radio-group class="radio-form" color="primary" - [formControl]="field.control"> - <mat-radio-button *ngFor="let value of field.values" - [value]="value.id"> - {{value.label}} - </mat-radio-button> - </mat-radio-group> - </ng-container> - <ng-container *ngIf="field.type === 'checkbox'"> - <mat-selection-list #shoes class="checkbox-form" - [formControl]="field.control"> - <mat-list-option *ngFor="let value of field.values" - [value]="value.id" color="primary" - checkboxPosition="before"> - {{value.label}} - </mat-list-option> - </mat-selection-list> - </ng-container> - </p> - <button *ngIf="field.display" [disabled]="!canDelete(field)" mat-icon-button matSuffix - color="warn" (click)="removeField(field)"> - <mat-icon class="fa fa-trash"></mat-icon> - </button> - </mat-list-item> - <ng-container - *ngIf="unit.id === 'address' && addressBANMode && ['addressAdditional1', 'addressAdditional2'].indexOf(field.id) > -1"> - <mat-list-item class="contact-item"> - <p mat-line class="contact-content" *ngIf="field.display"> - <mat-form-field> - <input matInput [formControl]="field.control" - [placeholder]="field.label" [required]="field.required"> - <mat-error *ngIf="field.control.hasError('required')"> - {{lang.requiredField}}</mat-error> - </mat-form-field> - </p> - <button *ngIf="field.display" [disabled]="!canDelete(field)" mat-icon-button - matSuffix color="warn" (click)="removeField(field)"> - <mat-icon class="fa fa-trash"></mat-icon> - </button> - </mat-list-item> - </ng-container> - <ng-container *ngIf="unit.id === 'address' && addressBANMode && i === 0"> - <mat-list-item> - <p mat-line class="contact-content"> - <mat-form-field appearance='outline' class="smallInput"> - <button mat-button matSuffix [matMenuTriggerFor]="menuDep" - (click)="$event.stopPropagation();" - [title]="lang.targetDepartment"> - {{addressBANCurrentDepartment}} <i - class="fa fa-chevron-down"></i> - </button> - <mat-menu #menuDep="matMenu"> - <button mat-menu-item *ngFor="let dep of departmentList" - (click)="addressBANCurrentDepartment = dep">{{dep}}</button> - </mat-menu> - <mat-icon color="primary" class="fa fa-search" matPrefix - style="font-size: 15px;"></mat-icon> - <input type="text" #autoCompleteInput - [placeholder]="lang.searchAddressBan" matInput - [formControl]="addressBANControl" [matAutocomplete]="auto" - (click)="$event.stopPropagation()" - (focus)="resetAutocompleteAddressBan()" maxlength="128"> - <mat-autocomplete #auto="matAutocomplete" - (optionSelected)="selectAddressBan($event)"> - <ng-container - *ngIf="addressBANResult.length > 0 && !addressBANLoading"> - <mat-option - *ngFor="let addressBANResult of addressBANFilteredResult | async" - [value]="addressBANResult"> - {{addressBANResult.address}} - </mat-option> - </ng-container> - <mat-option class="autoCompleteInfoResult smallInputInfo" - *ngIf="addressBANResult.length === 0 && !addressBANLoading" - disabled [innerHTML]="addressBANInfo"> - </mat-option> - <mat-option *ngIf="addressBANLoading" disabled> - <mat-spinner diameter="20"></mat-spinner> - </mat-option> - </mat-autocomplete> - </mat-form-field> - <mat-card style="margin:10px;" *ngIf="!emptyAddress()"> - <mat-list-item class="contact-address" (click)="goTo(contact)" - [title]="lang.address"> - <mat-icon mat-list-icon color="primary" - class="contact-group fas fa-map-marker-alt"></mat-icon> - <p mat-line class="contact-content"> - {{getValue('addressNumber')}} - {{getValue('addressStreet')}} - </p> - <p mat-line class="contact-content"> - {{getValue('addressPostcode')}} - {{getValue('addressTown')}} - </p> - <p mat-line class="contact-content"> - {{getValue('addressCountry')}} </p> - </mat-list-item> - </mat-card> - </p> - </mat-list-item> - </ng-container> - </ng-container> - </mat-list> - </div> - </ng-container> - </div> - <div style="text-align:center;"> - <button mat-raised-button color="default" type="button" - [matMenuTriggerFor]="menu">{{lang.moreInfos}}...</button> - - <button mat-raised-button color="primary" type="button" - (click)="onSubmit()">{{lang.validate}}</button> - </div> - </ng-template> + <app-contact-form *ngIf="!loading" [creationMode]="creationMode" [contactId]="contactId" (onSubmitEvent)="goToList()"></app-contact-form> </div> </div> </mat-sidenav-content> diff --git a/src/frontend/app/administration/contact/page/contacts-page-administration.component.scss b/src/frontend/app/administration/contact/page/contacts-page-administration.component.scss index 23cec77a17d6033dced3a3ceb955110c0886967d..b10a3ef8c289c653453e3c9bb434bf6285a83e95 100644 --- a/src/frontend/app/administration/contact/page/contacts-page-administration.component.scss +++ b/src/frontend/app/administration/contact/page/contacts-page-administration.component.scss @@ -3,120 +3,4 @@ .active, .active:hover , .active:active, .active:focus { background-color: $secondary; color: white; -} - -.contact-item { - height: auto !important; -} - -.multipleUnits div:nth-child(1) { - display: inline-grid !important; - width: 50%; -} - -.multipleUnits div:nth-child(2) { - display: inline-grid !important; - width: 50%; -} - -.unitTitle { - display: flex; - color: $primary; -} - -.contact-address { - cursor: pointer; - color: #337ab7; - - &:hover { - .contact-content { - text-decoration: underline; - } - } -} - -.smallInput { - font-size: 11px; - padding-left: 20px; - padding-right: 20px; - .mat-button { - width: 30px; - height: 25px; - color: $primary; - - ::ng-deep.mat-button-wrapper { - display: flex; - line-height: initial; - align-items: center; - } - } - ::ng-deep.mat-form-field-infix { - padding : 0px; - padding-bottom: 5px; - } -} -.radio-form { - display: flex; - - .mat-radio-button { - flex: 1; - } -} - -.checkbox-form { - overflow: auto; - max-height: 200px; -} - -.loading { - display: flex; - height: 100%; -} - -.fillingBar { - ::ng-deep .mat-progress-bar-buffer { - background: #E4E8EB; - } -} - -.rate_primary { - font-size: 10px; - text-align: right; - color: orange; -} - -.rate_warn { - font-size: 10px; - text-align: right; - color: red; -} - -.rate_accent { - font-size: 10px; - text-align: right; - color: green; -} - -.fillingBar.mat-warn { - ::ng-deep.mat-progress-bar-fill::after { - background-color: red; - } -} - -.fillingBar.mat-primary { - ::ng-deep.mat-progress-bar-fill::after { - background-color: orange; - } -} - -.fillingBar.mat-accent { - ::ng-deep.mat-progress-bar-fill::after { - background-color: green; - } -} - -.mat-error { - font-size: 10px; - text-align: right; - font-weight: bold; } \ No newline at end of file diff --git a/src/frontend/app/administration/contact/page/contacts-page-administration.component.ts b/src/frontend/app/administration/contact/page/contacts-page-administration.component.ts index 59a50e98ed7b900fc027c4c442582eaa72f4bc47..9eba00d5a4b0ce1446a5a2ad3ea5dc0b660a901b 100644 --- a/src/frontend/app/administration/contact/page/contacts-page-administration.component.ts +++ b/src/frontend/app/administration/contact/page/contacts-page-administration.component.ts @@ -1,15 +1,11 @@ -import { Component, OnInit, ViewChild, EventEmitter } from '@angular/core'; +import { Component, OnInit, ViewChild } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { LANG } from '../../../translate.component'; import { NotificationService } from '../../../notification.service'; import { HeaderService } from '../../../../service/header.service'; import { MatSidenav } from '@angular/material/sidenav'; import { AppService } from '../../../../service/app.service'; -import { Observable, merge, Subject, of as observableOf, of } from 'rxjs'; -import { MatPaginator, MatSort, MatDialog } from '@angular/material'; -import { takeUntil, startWith, switchMap, map, catchError, filter, exhaustMap, tap, debounceTime, distinctUntilChanged, finalize, count } from 'rxjs/operators'; -import { ConfirmComponent } from '../../../../plugins/modal/confirm.component'; -import { FormControl, Validators, ValidatorFn, FormGroup, FormBuilder } from '@angular/forms'; +import { MatDialog } from '@angular/material'; import { ActivatedRoute, Router } from '@angular/router'; @Component({ @@ -56,210 +52,17 @@ export class ContactsPageAdministrationComponent implements OnInit { contactId: number = null; - contactUnit = [ - { - id: 'mainInfo', - label: this.lang.denomination - }, - { - id: 'address', - label: this.lang.address - }, - { - id: 'complement', - label: this.lang.additionals - } - ]; - - contactForm: any[] = [ - { - id: 'company', - unit: 'mainInfo', - label: this.lang.contactsParameters_company, - type: 'string', - control: new FormControl(), - required: false, - display: true, - filling: false, - values: [] - }, - { - id: 'firstname', - unit: 'mainInfo', - label: this.lang.contactsParameters_firstname, - type: 'string', - control: new FormControl(), - required: false, - display: false, - filling: false, - values: [] - }, - { - id: 'lastname', - unit: 'mainInfo', - label: this.lang.contactsParameters_lastname, - type: 'string', - control: new FormControl(), - required: false, - display: false, - filling: false, - values: [] - }, - { - id: 'function', - unit: 'mainInfo', - label: this.lang.contactsParameters_function, - type: 'string', - control: new FormControl(), - required: false, - display: false, - filling: false, - values: [] - }, - { - id: 'department', - unit: 'mainInfo', - label: this.lang.contactsParameters_department, - type: 'string', - control: new FormControl(), - required: false, - display: false, - filling: false, - values: [] - }, - { - id: 'email', - unit: 'mainInfo', - label: this.lang.email, - type: 'string', - control: new FormControl(), - required: false, - display: true, - filling: false, - values: [] - }, - { - id: 'phone', - unit: 'mainInfo', - label: this.lang.phoneNumber, - type: 'string', - control: new FormControl(), - required: false, - display: true, - filling: false, - values: [] - }, - { - id: 'addressNumber', - unit: 'address', - label: this.lang.contactsParameters_addressNumber, - type: 'string', - control: new FormControl(), - required: false, - display: false, - filling: false, - values: [] - }, - { - id: 'addressStreet', - unit: 'address', - label: this.lang.contactsParameters_addressStreet, - type: 'string', - control: new FormControl(), - required: false, - display: false, - filling: false, - values: [] - }, - { - id: 'addressAdditional1', - unit: 'address', - label: this.lang.contactsParameters_addressAdditional1, - type: 'string', - control: new FormControl(), - required: false, - display: false, - filling: false, - values: [] - }, - { - id: 'addressAdditional2', - unit: 'address', - label: this.lang.contactsParameters_addressAdditional2, - type: 'string', - control: new FormControl(), - required: false, - display: false, - filling: false, - values: [] - }, - { - id: 'addressPostcode', - unit: 'address', - label: this.lang.contactsParameters_addressPostcode, - type: 'string', - control: new FormControl(), - required: false, - display: false, - filling: false, - values: [] - }, - { - id: 'addressTown', - unit: 'address', - label: this.lang.contactsParameters_addressTown, - type: 'string', - control: new FormControl(), - required: false, - display: false, - filling: false, - values: [] - }, - { - id: 'addressCountry', - unit: 'address', - label: this.lang.contactsParameters_addressCountry, - type: 'string', - control: new FormControl(), - required: false, - display: false, - filling: false, - values: [] - } - ]; - - addressBANInfo: string = ''; - addressBANMode: boolean = true; - addressBANControl = new FormControl(); - addressBANLoading: boolean = false; - addressBANResult: any[] = []; - addressBANFilteredResult: Observable<string[]>; - addressBANCurrentDepartment: string = '75'; - departmentList: any[] = []; - - fillingParameters: any = null; - fillingRate: any = { - class: 'warn', - value : 0 - } - - companyFound: any = null; - constructor( public http: HttpClient, private route: ActivatedRoute, private router: Router, - private notify: NotificationService, private headerService: HeaderService, public appService: AppService, - public dialog: MatDialog, - private formBuilder: FormBuilder) { } + public dialog: MatDialog) { } ngOnInit(): void { this.loading = true; - - this.initBanSearch(); this.route.params.subscribe((params: any) => { if (typeof params['id'] == "undefined") { @@ -268,23 +71,8 @@ export class ContactsPageAdministrationComponent implements OnInit { this.headerService.setHeader(this.lang.contactCreation); this.creationMode = true; + this.loading = false; - this.http.get("../../rest/contactsCustomFields").pipe( - tap((data: any) => { - this.initCustomElementForm(data); - }), - exhaustMap(() => this.http.get("../../rest/contactsParameters")), - tap((data: any) => { - this.fillingParameters = data.contactsFilling; - this.initElemForm(data); - this.initAutocompleteAddressBan(); - }), - finalize(() => this.loading = false), - catchError((err: any) => { - this.notify.handleErrors(err); - return of(false); - }) - ).subscribe(); } else { window['MainHeaderComponent'].setSnav(this.sidenavLeft); window['MainHeaderComponent'].setSnavRight(this.sidenavRight); @@ -293,436 +81,14 @@ export class ContactsPageAdministrationComponent implements OnInit { this.creationMode = false; - this.contactForm.forEach(element => { - element.display = false; - }); - - this.http.get("../../rest/contactsCustomFields").pipe( - tap((data: any) => { - this.initCustomElementForm(data); - }), - exhaustMap(() => this.http.get("../../rest/contactsParameters")), - tap((data: any) => { - this.fillingParameters = data.contactsFilling; - this.initElemForm(data); - this.initAutocompleteAddressBan(); - }), - exhaustMap(() => this.http.get("../../rest/contacts/" + params['id'])), - tap((data) => { - this.contactId = params['id']; - this.setContactData(data); - }), - filter((data: any) => data.customFields !== null), - tap((data: any) => { - this.setContactCustomData(data); - }), - finalize(() => this.loading = false), - catchError((err: any) => { - this.notify.handleErrors(err); - return of(false); - }) - ).subscribe(); - } - }); - } - - initElemForm(data: any) { - let valArr: ValidatorFn[] = []; - - data.contactsParameters.forEach((element: any) => { - valArr = []; - - if ((element.mandatory || element.filling) && this.creationMode) { - this.contactForm.filter(contact => contact.id === element.identifier)[0].display = true; - } - - if (element.filling) { - this.contactForm.filter(contact => contact.id === element.identifier)[0].filling = true; - } - - if (element.identifier === 'email') { - valArr.push(Validators.email); - } else if (element.identifier === 'phone') { - valArr.push(Validators.pattern(/\+?((|\ |\.|\(|\)|\-)?(\d)*)*\d$/)); - } - - if (element.mandatory) { - this.contactForm.filter(contact => contact.id === element.identifier)[0].required = true; - valArr.push(Validators.required); - } - - if(this.contactForm.filter(contact => contact.id === element.identifier)[0] !== undefined) { - this.contactForm.filter(contact => contact.id === element.identifier)[0].control.setValidators(valArr); - } - - }); - } - - initCustomElementForm(data: any) { - let valArr: ValidatorFn[] = []; - - let field: any = {}; - - data.customFields.forEach((element: any) => { - valArr = []; - - field = { - id: `customField_${element.id}`, - unit: 'complement', - label: element.label, - type: element.type, - control: new FormControl({ value: '', disabled: false }), - required: false, - display: false, - values: element.values.map((val: any) => { return { id: val, label: val } }) - }; - - if (element.type === 'integer') { - valArr.push(Validators.pattern(/^[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)$/)); - field.control.setValidators(valArr); - } - this.contactForm.push(field); - }); - } - - setContactData(data: any) { - let indexField = -1; - Object.keys(data).forEach(element => { - indexField = this.contactForm.map(field => field.id).indexOf(element); - if (!this.isEmptyValue(data[element]) && indexField > -1) { - this.contactForm[indexField].control.setValue(data[element]); - this.contactForm[indexField].display = true; - } - }); - this.checkFilling(); - } - - setContactCustomData(data: any) { - let indexField = -1; - Object.keys(data.customFields).forEach(element => { - indexField = this.contactForm.map(field => field.id).indexOf('customField_' + element); - if (!this.isEmptyValue(data[element]) && indexField > -1) { - this.contactForm[indexField].control.setValue(data.customFields[element]); - this.contactForm[indexField].display = true; - } - }); - this.checkFilling(); - } - - initBanSearch() { - this.http.get("../../rest/ban/availableDepartments").pipe( - tap((data: any) => { - if (data.default !== null && data.departments.indexOf(data.default.toString()) !== - 1) { - this.addressBANCurrentDepartment = data.default; - } - this.departmentList = data.departments; - }), - catchError((err: any) => { - this.notify.handleErrors(err); - return of(false); - }) - ).subscribe(); - } - - isValidForm() { - let state = true; - - this.contactForm.filter(contact => contact.display).forEach(element => { - if (element.control.status !== 'VALID') { - state = false; - } - element.control.markAsTouched() - }); - - return state; - } - - onSubmit() { - this.checkFilling(); - if (this.addressBANMode && this.emptyAddress()) { - this.notify.error('Choisissez une BAN'); - } else if (this.isValidForm()) { - if (this.contactId !== null) { - this.updateContact(); - } else { - this.createContact(); - } - } else { - this.notify.error('Veuillez corriger les erreurs'); - } - - } - - createContact() { - this.http.post("../../rest/contacts", this.formatContact()).pipe( - tap(() => { - this.router.navigate(["/administration/contacts/list"]); - this.notify.success(this.lang.contactAdded); - }), - //finalize(() => this.loading = false), - catchError((err: any) => { - this.notify.handleErrors(err); - return of(false); - }) - ).subscribe(); - } - - updateContact() { - this.http.put(`../../rest/contacts/${this.contactId}`, this.formatContact()).pipe( - tap(() => { - this.router.navigate(["/administration/contacts/list"]); - this.notify.success(this.lang.contactUpdated); - }), - //finalize(() => this.loading = false), - catchError((err: any) => { - this.notify.handleErrors(err); - return of(false); - }) - ).subscribe(); - } - - formatContact() { - let contact: any = {}; - contact['customFields'] = {}; - const regex = /customField_[.]*/g; + this.contactId = params['id']; - this.contactForm.filter(field => field.display).forEach(element => { - if (element.id.match(regex) !== null) { - contact['customFields'][element.id.split('_')[1]] = element.control.value; - } else { - contact[element.id] = element.control.value; + this.loading = false; } }); - return contact; - } - - isEmptyUnit(id: string) { - if (this.contactForm.filter(field => field.display && field.unit === id).length === 0) { - return true; - } else { - return false; - } - } - - initForm() { - this.contactForm.forEach(element => { - element.control = new FormControl({ value: '', disabled: false }); - }); - } - - toogleAllFieldsUnit(idUnit: string) { - this.contactForm.filter(field => field.unit === idUnit).forEach((element: any) => { - element.display = true; - }); - } - - noField(id: string) { - if (this.contactForm.filter(field => !field.display && field.unit === id).length === 0) { - return true; - } else { - return false; - } - } - - isEmptyValue(value: string) { - - if (value === null) { - return true; - - } else if (Array.isArray(value)) { - if (value.length > 0) { - return false; - } else { - return true; - } - } else if (String(value) !== '') { - return false; - } else { - return true; - } } - checkCompany(field: any) { - if (field.id === 'company' && (this.companyFound === null || this.companyFound.company !== field.control.value)) { - - this.http.get(`../../rest/autocomplete/contacts/company?search=${field.control.value}`).pipe( - tap(() => this.companyFound = null), - filter((data: any) => data.length > 0), - tap((data) => { - this.companyFound = data[0]; - }), - //finalize(() => this.loading = false), - catchError((err: any) => { - this.notify.handleErrors(err); - return of(false); - }) - ).subscribe(); - } - } - - setAddress(contact: any, disableBan: boolean = true) { - let indexField = -1; - Object.keys(contact).forEach(element => { - indexField = this.contactForm.map(field => field.id).indexOf(element); - if (!this.isEmptyValue(contact[element]) && indexField > -1 && ['company', 'addressNumber', 'addressStreet', 'addressAdditional2', 'addressPostcode', 'addressTown', 'addressCountry'].indexOf(element) > -1) { - this.contactForm[indexField].control.setValue(contact[element]); - this.contactForm[indexField].display = true; - } - }); - this.checkFilling(); - - this.addressBANMode = disableBan ? false : true; - } - - canDelete(field: any) { - if (field.id === "company") { - const lastname = this.contactForm.filter(contact => contact.id === 'lastname')[0]; - if (lastname.display && !this.isEmptyValue(lastname.control.value)) { - let valArr: ValidatorFn[] = []; - field.control.setValidators(valArr); - field.required = false; - return true; - } else { - let valArr: ValidatorFn[] = []; - valArr.push(Validators.required); - field.control.setValidators(valArr); - field.required = true; - return false; - } - } else if (field.id === "lastname") { - const company = this.contactForm.filter(contact => contact.id === 'company')[0]; - if (company.display && !this.isEmptyValue(company.control.value)) { - let valArr: ValidatorFn[] = []; - field.control.setValidators(valArr); - field.required = false; - return true; - } else { - let valArr: ValidatorFn[] = []; - valArr.push(Validators.required); - field.control.setValidators(valArr); - field.required = true; - return false; - } - } else if (field.required) { - return false; - } else { - return true; - } - } - - removeField(field: any) { - field.display = !field.display; - field.control.reset(); - this.checkFilling(); - } - - initAutocompleteAddressBan() { - this.addressBANInfo = this.lang.autocompleteInfo; - this.addressBANResult = []; - this.addressBANControl.valueChanges - .pipe( - debounceTime(300), - filter(value => value.length > 2), - distinctUntilChanged(), - tap(() => this.addressBANLoading = true), - switchMap((data: any) => this.http.get('../../rest/autocomplete/banAddresses', { params: { "address": data, 'department': this.addressBANCurrentDepartment } })), - tap((data: any) => { - if (data.length === 0) { - this.addressBANInfo = this.lang.noAvailableValue; - } else { - this.addressBANInfo = ''; - } - this.addressBANResult = data; - this.addressBANFilteredResult = of(this.addressBANResult); - this.addressBANLoading = false; - }) - ).subscribe(); - } - - resetAutocompleteAddressBan() { - this.addressBANResult = []; - this.addressBANInfo = this.lang.autocompleteInfo; - } - - selectAddressBan(ev: any) { - let contact = { - addressNumber: ev.option.value.number, - addressStreet: ev.option.value.afnorName, - addressPostcode: ev.option.value.postalCode, - addressTown: ev.option.value.city, - addressCountry: 'FRANCE' - }; - this.setAddress(contact, false); - this.addressBANControl.setValue(''); - } - - getValue(identifier: string) { - return this.contactForm.filter(contact => contact.id === identifier)[0].control.value; - } - - emptyAddress() { - if (this.contactForm.filter(contact => this.isEmptyValue(contact.control.value) && ['addressNumber', 'addressStreet', 'addressPostcode', 'addressTown', 'addressCountry'].indexOf(contact.id) > -1).length > 0) { - return true; - } else { - return false; - } - } - - goTo() { - const contact = { - addressNumber: this.contactForm.filter(contact => contact.id === 'addressNumber')[0].control.value, - addressStreet: this.contactForm.filter(contact => contact.id === 'addressStreet')[0].control.value, - addressPostcode: this.contactForm.filter(contact => contact.id === 'addressPostcode')[0].control.value, - addressTown: this.contactForm.filter(contact => contact.id === 'addressTown')[0].control.value, - addressCountry: this.contactForm.filter(contact => contact.id === 'addressCountry')[0].control.value - }; - window.open(`https://www.google.com/maps/search/${contact.addressNumber}+${contact.addressStreet},+${contact.addressPostcode}+${contact.addressTown},+${contact.addressCountry}`, '_blank') - } - - switchAddressMode() { - let valArr: ValidatorFn[] = []; - if (this.addressBANMode) { - - valArr.push(Validators.required); - - this.contactForm.filter(contact => ['addressNumber', 'addressStreet', 'addressPostcode', 'addressTown', 'addressCountry'].indexOf(contact.id) > -1).forEach((element: any) => { - if (element.mandatory) { - element.control.setValidators(valArr); - } - }); - this.addressBANMode = !this.addressBANMode; - } else { - this.contactForm.filter(contact => ['addressNumber', 'addressStreet', 'addressPostcode', 'addressTown', 'addressCountry'].indexOf(contact.id) > -1).forEach((element: any) => { - if (element.mandatory) { - element.control.setValidators(valArr); - } - }); - this.addressBANMode = !this.addressBANMode; - } - } - - getErrorMsg(error: any) { - if (error.required !== undefined) { - return this.lang.requiredField; - } else if (error.pattern !== undefined || error.email !== undefined) { - return this.lang.badFormat; - } else { - return 'unknow validator'; - } - } - - checkFilling() { - const countFilling = this.contactForm.filter(contact => contact.filling).length; - const countValNotEmpty = this.contactForm.filter(contact => !this.isEmptyValue(contact.control.value) && contact.filling).length; - - this.fillingRate.value = Math.round((countValNotEmpty * 100) / countFilling); - - if (this.fillingRate.value <= this.fillingParameters.first_threshold) { - this.fillingRate.class = 'warn'; - } else if (this.fillingRate.value <= this.fillingParameters.second_threshold) { - this.fillingRate.class = 'primary'; - } else { - this.fillingRate.class = 'accent'; - } + goToList() { + this.router.navigate(["/administration/contacts/list"]); } } diff --git a/src/frontend/app/administration/contact/page/form/contacts-form.component.html b/src/frontend/app/administration/contact/page/form/contacts-form.component.html new file mode 100644 index 0000000000000000000000000000000000000000..399ba5e74c94509a5facd54e552acc9df224924c --- /dev/null +++ b/src/frontend/app/administration/contact/page/form/contacts-form.component.html @@ -0,0 +1,226 @@ +<div class="loading" *ngIf="loading; else elseTemplate"> + <mat-spinner style="margin:auto;"></mat-spinner> +</div> +<ng-template #elseTemplate> + <mat-menu #menu="matMenu"> + <button mat-menu-item [matMenuTriggerFor]="mainInfo" + [disabled]="noField('mainInfo')">{{lang.denomination}}</button> + <button mat-menu-item [matMenuTriggerFor]="address" [disabled]="noField('address')">{{lang.address}}</button> + <button mat-menu-item [matMenuTriggerFor]="complement" + [disabled]="noField('complement')">{{lang.additionals}}</button> + </mat-menu> + + <mat-menu #mainInfo="matMenu"> + <button mat-menu-item (click)="toogleAllFieldsUnit('mainInfo')" + style="text-align: center;">{{lang.addAll}}</button> + <mat-divider></mat-divider> + <ng-container *ngFor="let field of contactForm"> + <button mat-menu-item *ngIf="!field.display && field.unit === 'mainInfo'" + (click)="field.display=!field.display"> + <mat-icon *ngIf="field.filling" style="height: auto;padding-right: 10px;" + [title]="lang.targetFillingField" class="fas fa-circle rate_{{fillingRate.class}}"></mat-icon> + <span>{{field.label}}</span> + </button> + </ng-container> + </mat-menu> + + <mat-menu #address="matMenu"> + <button mat-menu-item (click)="toogleAllFieldsUnit('address')" + style="text-align: center;">{{lang.addAll}}</button> + <mat-divider></mat-divider> + <ng-container *ngFor="let field of contactForm"> + <button mat-menu-item *ngIf="!field.display && field.unit === 'address'" + (click)="field.display=!field.display"> + <mat-icon *ngIf="field.filling" style="height: auto;padding-right: 10px;" + [title]="lang.targetFillingField" class="fas fa-circle rate_{{fillingRate.class}}"></mat-icon> + <span>{{field.label}}</span> + </button> + </ng-container> + </mat-menu> + <mat-menu #complement="matMenu"> + <button mat-menu-item (click)="toogleAllFieldsUnit('complement')" + style="text-align: center;">{{lang.addAll}}</button> + <mat-divider></mat-divider> + <ng-container *ngFor="let field of contactForm"> + <button mat-menu-item *ngIf="!field.display && field.unit === 'complement'" + (click)="field.display=!field.display"> + <mat-icon *ngIf="field.filling" style="height: auto;padding-right: 10px;" + [title]="lang.targetFillingField" class="fas fa-circle rate_{{fillingRate.class}}"></mat-icon> + <span>{{field.label}}</span> + </button> + </ng-container> + </mat-menu> + <div style="padding: 10px;"> + <div class="rate_{{fillingRate.class}}">{{lang.contactFilledTo}} <b>{{fillingRate.value}} %</b></div> + <mat-progress-bar mode="determinate" class="fillingBar" [color]="fillingRate.class" [value]="fillingRate.value"> + </mat-progress-bar> + + </div> + <div [class.multipleUnits]="!isEmptyUnit('address') || !isEmptyUnit('complement')"> + <ng-container *ngFor="let unit of contactUnit"> + <div *ngIf="!isEmptyUnit(unit.id)"> + <mat-list> + <h3 mat-subheader class="unitTitle"><span style="flex:1">{{unit.label}}</span><a + *ngIf="unit.id === 'address'" (click)="addressBANMode=!addressBANMode" + style="cursor: pointer;">{{addressBANMode ? lang.switchManualAddress : lang.searchAddressDb}}</a> + </h3> + <ng-container *ngFor="let field of contactForm;let i=index"> + <mat-list-item class="contact-item" + *ngIf="(field.unit === unit.id && unit.id !== 'address') || (field.unit === unit.id && unit.id === 'address' && !addressBANMode)"> + <p mat-line class="contact-content" *ngIf="field.display"> + <ng-container *ngIf="field.type === 'string'"> + <mat-form-field> + <input matInput [formControl]="field.control" [placeholder]="field.label" + (blur)="checkCompany(field);checkFilling();" [required]="field.required"> + <mat-hint *ngIf="companyFound !== null && field.id === 'company'" align="end"> + {{lang.contactsParameters_company}} <b>{{companyFound.company}}</b> + {{lang.found}} + ! + cliquez <a (click)="setAddress(companyFound)" + style="cursor: pointer;font-weight:bold;">{{lang.here}}</a> + {{lang.toCopyAddress}}</mat-hint> + <mat-error *ngIf="field.control.status!=='VALID' && field.control.touched"> + {{getErrorMsg(field.control.errors)}}</mat-error> + <mat-icon style="height: auto;" matSuffix [title]="lang.targetFillingField" + class="fas fa-circle rate_{{fillingRate.class}}"></mat-icon> + </mat-form-field> + </ng-container> + <ng-container *ngIf="field.type === 'integer'"> + <mat-form-field> + <input type="text" matInput [formControl]="field.control" + [placeholder]="field.label" min="0" step="0.1" [required]="field.required"> + <mat-error *ngIf="field.control.status!=='VALID' && field.control.touched"> + {{getErrorMsg(field.control.errors)}}</mat-error> + </mat-form-field> + </ng-container> + <ng-container *ngIf="field.type === 'select'"> + <plugin-select-search [label]="field.label" [showLabel]="true" + [formControlSelect]="field.control" [placeholderLabel]="field.label" + [datas]="field.values" [class]="''"> + </plugin-select-search> + </ng-container> + <ng-container *ngIf="field.type === 'date'"> + <mat-form-field (click)="picker.open()" style="cursor:pointer;"> + <mat-label>{{field.label}} + </mat-label> + <input [formControl]="field.control" matInput [matDatepicker]="picker" + [placeholder]="field.label" readonly style="cursor:pointer;"> + <mat-datepicker-toggle matSuffix [for]="picker" *ngIf="!field.control.value"> + </mat-datepicker-toggle> + <mat-datepicker [touchUi]="appService.getViewMode()" #picker> + </mat-datepicker> + <button mat-button color="warn" matSuffix mat-icon-button + *ngIf="field.control.value && !field.control.disabled" + (click)="$event.stopPropagation();field.control.reset();" + [title]="lang.eraseValue"> + <mat-icon color="warn" class="fa fa-calendar-times"> + </mat-icon> + </button> + </mat-form-field> + </ng-container> + <ng-container *ngIf="field.type === 'radio'"> + <mat-radio-group class="radio-form" color="primary" [formControl]="field.control"> + <mat-radio-button *ngFor="let value of field.values" [value]="value.id"> + {{value.label}} + </mat-radio-button> + </mat-radio-group> + </ng-container> + <ng-container *ngIf="field.type === 'checkbox'"> + <mat-selection-list #shoes class="checkbox-form" [formControl]="field.control"> + <mat-list-option *ngFor="let value of field.values" [value]="value.id" + color="primary" checkboxPosition="before"> + {{value.label}} + </mat-list-option> + </mat-selection-list> + </ng-container> + </p> + <button *ngIf="field.display" [disabled]="!canDelete(field)" mat-icon-button matSuffix + color="warn" (click)="removeField(field)"> + <mat-icon class="fa fa-trash"></mat-icon> + </button> + </mat-list-item> + <ng-container + *ngIf="unit.id === 'address' && addressBANMode && ['addressAdditional1', 'addressAdditional2'].indexOf(field.id) > -1"> + <mat-list-item class="contact-item"> + <p mat-line class="contact-content" *ngIf="field.display"> + <mat-form-field> + <input matInput [formControl]="field.control" [placeholder]="field.label" + [required]="field.required"> + <mat-error *ngIf="field.control.hasError('required')"> + {{lang.requiredField}}</mat-error> + </mat-form-field> + </p> + <button *ngIf="field.display" [disabled]="!canDelete(field)" mat-icon-button matSuffix + color="warn" (click)="removeField(field)"> + <mat-icon class="fa fa-trash"></mat-icon> + </button> + </mat-list-item> + </ng-container> + <ng-container *ngIf="unit.id === 'address' && addressBANMode && i === 0"> + <mat-list-item> + <p mat-line class="contact-content"> + <mat-form-field appearance='outline' class="smallInput"> + <button mat-button matSuffix [matMenuTriggerFor]="menuDep" + (click)="$event.stopPropagation();" [title]="lang.targetDepartment"> + {{addressBANCurrentDepartment}} <i class="fa fa-chevron-down"></i> + </button> + <mat-menu #menuDep="matMenu"> + <button mat-menu-item *ngFor="let dep of departmentList" + (click)="addressBANCurrentDepartment = dep">{{dep}}</button> + </mat-menu> + <mat-icon color="primary" class="fa fa-search" matPrefix + style="font-size: 15px;"></mat-icon> + <input type="text" #autoCompleteInput [placeholder]="lang.searchAddressBan" + matInput [formControl]="addressBANControl" [matAutocomplete]="auto" + (click)="$event.stopPropagation()" (focus)="resetAutocompleteAddressBan()" + maxlength="128"> + <mat-autocomplete #auto="matAutocomplete" + (optionSelected)="selectAddressBan($event)"> + <ng-container *ngIf="addressBANResult.length > 0 && !addressBANLoading"> + <mat-option + *ngFor="let addressBANResult of addressBANFilteredResult | async" + [value]="addressBANResult"> + {{addressBANResult.address}} + </mat-option> + </ng-container> + <mat-option class="autoCompleteInfoResult smallInputInfo" + *ngIf="addressBANResult.length === 0 && !addressBANLoading" disabled + [innerHTML]="addressBANInfo"> + </mat-option> + <mat-option *ngIf="addressBANLoading" disabled> + <mat-spinner diameter="20"></mat-spinner> + </mat-option> + </mat-autocomplete> + </mat-form-field> + <mat-card style="margin:10px;" *ngIf="!emptyAddress()"> + <mat-list-item class="contact-address" (click)="goTo(contact)" + [title]="lang.address"> + <mat-icon mat-list-icon color="primary" + class="contact-group fas fa-map-marker-alt"></mat-icon> + <p mat-line class="contact-content"> + {{getValue('addressNumber')}} + {{getValue('addressStreet')}} + </p> + <p mat-line class="contact-content"> + {{getValue('addressPostcode')}} + {{getValue('addressTown')}} + </p> + <p mat-line class="contact-content"> + {{getValue('addressCountry')}} </p> + </mat-list-item> + </mat-card> + </p> + </mat-list-item> + </ng-container> + </ng-container> + </mat-list> + </div> + </ng-container> + </div> + <div style="text-align:center;"> + <button mat-raised-button color="default" type="button" + [matMenuTriggerFor]="menu">{{lang.moreInfos}}...</button> + + <button mat-raised-button color="primary" type="button" (click)="onSubmit()">{{lang.validate}}</button> + </div> +</ng-template> \ No newline at end of file diff --git a/src/frontend/app/administration/contact/page/form/contacts-form.component.scss b/src/frontend/app/administration/contact/page/form/contacts-form.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..d6412bdd07d8ba60c2a00c6d68aca002d89ee639 --- /dev/null +++ b/src/frontend/app/administration/contact/page/form/contacts-form.component.scss @@ -0,0 +1,117 @@ +@import '../../../../../css/vars.scss'; + +.contact-item { + height: auto !important; +} + +.multipleUnits div:nth-child(1) { + display: inline-grid !important; + width: 50%; +} + +.multipleUnits div:nth-child(2) { + display: inline-grid !important; + width: 50%; +} + +.unitTitle { + display: flex; + color: $primary; +} + +.contact-address { + cursor: pointer; + color: #337ab7; + + &:hover { + .contact-content { + text-decoration: underline; + } + } +} + +.smallInput { + font-size: 11px; + padding-left: 20px; + padding-right: 20px; + .mat-button { + width: 30px; + height: 25px; + color: $primary; + + ::ng-deep.mat-button-wrapper { + display: flex; + line-height: initial; + align-items: center; + } + } + ::ng-deep.mat-form-field-infix { + padding : 0px; + padding-bottom: 5px; + } +} +.radio-form { + display: flex; + + .mat-radio-button { + flex: 1; + } +} + +.checkbox-form { + overflow: auto; + max-height: 200px; +} + +.loading { + display: flex; + height: 100%; +} + +.fillingBar { + ::ng-deep .mat-progress-bar-buffer { + background: #E4E8EB; + } +} + +.rate_primary { + font-size: 10px; + text-align: right; + color: orange; +} + +.rate_warn { + font-size: 10px; + text-align: right; + color: red; +} + +.rate_accent { + font-size: 10px; + text-align: right; + color: green; +} + +.fillingBar.mat-warn { + ::ng-deep.mat-progress-bar-fill::after { + background-color: red; + } +} + +.fillingBar.mat-primary { + ::ng-deep.mat-progress-bar-fill::after { + background-color: orange; + } +} + +.fillingBar.mat-accent { + ::ng-deep.mat-progress-bar-fill::after { + background-color: green; + } +} + +.mat-error { + font-size: 10px; + text-align: right; + font-weight: bold; +} \ No newline at end of file diff --git a/src/frontend/app/administration/contact/page/form/contacts-form.component.ts b/src/frontend/app/administration/contact/page/form/contacts-form.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..2250f51b373a7bc4ab81d1f1412bdf08c9a7efdc --- /dev/null +++ b/src/frontend/app/administration/contact/page/form/contacts-form.component.ts @@ -0,0 +1,691 @@ +import { Component, OnInit, ViewChild, EventEmitter, Input, Output } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { LANG } from '../../../../translate.component'; +import { NotificationService } from '../../../../notification.service'; +import { HeaderService } from '../../../../../service/header.service'; +import { MatSidenav } from '@angular/material/sidenav'; +import { AppService } from '../../../../../service/app.service'; +import { Observable, merge, Subject, of as observableOf, of } from 'rxjs'; +import { MatPaginator, MatSort, MatDialog } from '@angular/material'; +import { startWith, switchMap, map, catchError, filter, exhaustMap, tap, debounceTime, distinctUntilChanged, finalize, count } from 'rxjs/operators'; +import { FormControl, Validators, ValidatorFn, FormGroup, FormBuilder } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; + +@Component({ + selector: 'app-contact-form', + templateUrl: "contacts-form.component.html", + styleUrls: ['contacts-form.component.scss'], + providers: [NotificationService, AppService] +}) +export class ContactsFormComponent implements OnInit { + + @ViewChild('snav', { static: true }) public sidenavLeft: MatSidenav; + @ViewChild('snav2', { static: true }) public sidenavRight: MatSidenav; + + lang: any = LANG; + loading: boolean = false; + + @Input() creationMode: boolean = true; + @Input() contactId: number = null; + + @Output() onSubmitEvent = new EventEmitter<number>(); + + contactUnit = [ + { + id: 'mainInfo', + label: this.lang.denomination + }, + { + id: 'address', + label: this.lang.address + }, + { + id: 'complement', + label: this.lang.additionals + } + ]; + + contactForm: any[] = [ + { + id: 'company', + unit: 'mainInfo', + label: this.lang.contactsParameters_company, + type: 'string', + control: new FormControl(), + required: false, + display: true, + filling: false, + values: [] + }, + { + id: 'firstname', + unit: 'mainInfo', + label: this.lang.contactsParameters_firstname, + type: 'string', + control: new FormControl(), + required: false, + display: false, + filling: false, + values: [] + }, + { + id: 'lastname', + unit: 'mainInfo', + label: this.lang.contactsParameters_lastname, + type: 'string', + control: new FormControl(), + required: false, + display: false, + filling: false, + values: [] + }, + { + id: 'function', + unit: 'mainInfo', + label: this.lang.contactsParameters_function, + type: 'string', + control: new FormControl(), + required: false, + display: false, + filling: false, + values: [] + }, + { + id: 'department', + unit: 'mainInfo', + label: this.lang.contactsParameters_department, + type: 'string', + control: new FormControl(), + required: false, + display: false, + filling: false, + values: [] + }, + { + id: 'email', + unit: 'mainInfo', + label: this.lang.email, + type: 'string', + control: new FormControl(), + required: false, + display: true, + filling: false, + values: [] + }, + { + id: 'phone', + unit: 'mainInfo', + label: this.lang.phoneNumber, + type: 'string', + control: new FormControl(), + required: false, + display: true, + filling: false, + values: [] + }, + { + id: 'addressNumber', + unit: 'address', + label: this.lang.contactsParameters_addressNumber, + type: 'string', + control: new FormControl(), + required: false, + display: false, + filling: false, + values: [] + }, + { + id: 'addressStreet', + unit: 'address', + label: this.lang.contactsParameters_addressStreet, + type: 'string', + control: new FormControl(), + required: false, + display: false, + filling: false, + values: [] + }, + { + id: 'addressAdditional1', + unit: 'address', + label: this.lang.contactsParameters_addressAdditional1, + type: 'string', + control: new FormControl(), + required: false, + display: false, + filling: false, + values: [] + }, + { + id: 'addressAdditional2', + unit: 'address', + label: this.lang.contactsParameters_addressAdditional2, + type: 'string', + control: new FormControl(), + required: false, + display: false, + filling: false, + values: [] + }, + { + id: 'addressPostcode', + unit: 'address', + label: this.lang.contactsParameters_addressPostcode, + type: 'string', + control: new FormControl(), + required: false, + display: false, + filling: false, + values: [] + }, + { + id: 'addressTown', + unit: 'address', + label: this.lang.contactsParameters_addressTown, + type: 'string', + control: new FormControl(), + required: false, + display: false, + filling: false, + values: [] + }, + { + id: 'addressCountry', + unit: 'address', + label: this.lang.contactsParameters_addressCountry, + type: 'string', + control: new FormControl(), + required: false, + display: false, + filling: false, + values: [] + } + ]; + + addressBANInfo: string = ''; + addressBANMode: boolean = true; + addressBANControl = new FormControl(); + addressBANLoading: boolean = false; + addressBANResult: any[] = []; + addressBANFilteredResult: Observable<string[]>; + addressBANCurrentDepartment: string = '75'; + departmentList: any[] = []; + + fillingParameters: any = null; + fillingRate: any = { + class: 'warn', + value: 0 + } + + companyFound: any = null; + + constructor( + public http: HttpClient, + private route: ActivatedRoute, + private router: Router, + private notify: NotificationService, + private headerService: HeaderService, + public appService: AppService, + public dialog: MatDialog + ) { } + + ngOnInit(): void { + + this.loading = true; + + this.initBanSearch(); + + if (this.contactId === null) { + + this.creationMode = true; + + this.http.get("../../rest/contactsCustomFields").pipe( + tap((data: any) => { + this.initCustomElementForm(data); + }), + exhaustMap(() => this.http.get("../../rest/contactsParameters")), + tap((data: any) => { + this.fillingParameters = data.contactsFilling; + this.initElemForm(data); + this.initAutocompleteAddressBan(); + }), + finalize(() => this.loading = false), + catchError((err: any) => { + this.notify.handleErrors(err); + return of(false); + }) + ).subscribe(); + } else { + this.creationMode = false; + + this.contactForm.forEach(element => { + element.display = false; + }); + + this.http.get("../../rest/contactsCustomFields").pipe( + tap((data: any) => { + this.initCustomElementForm(data); + }), + exhaustMap(() => this.http.get("../../rest/contactsParameters")), + tap((data: any) => { + this.fillingParameters = data.contactsFilling; + this.initElemForm(data); + this.initAutocompleteAddressBan(); + }), + exhaustMap(() => this.http.get("../../rest/contacts/" + this.contactId)), + tap((data) => { + this.setContactData(data); + }), + filter((data: any) => data.customFields !== null), + tap((data: any) => { + this.setContactCustomData(data); + }), + finalize(() => this.loading = false), + catchError((err: any) => { + this.notify.handleErrors(err); + return of(false); + }) + ).subscribe(); + } + } + + initElemForm(data: any) { + let valArr: ValidatorFn[] = []; + + data.contactsParameters.forEach((element: any) => { + valArr = []; + + if ((element.mandatory || element.filling) && this.creationMode) { + this.contactForm.filter(contact => contact.id === element.identifier)[0].display = true; + } + + if (element.filling) { + this.contactForm.filter(contact => contact.id === element.identifier)[0].filling = true; + } + + if (element.identifier === 'email') { + valArr.push(Validators.email); + } else if (element.identifier === 'phone') { + valArr.push(Validators.pattern(/\+?((|\ |\.|\(|\)|\-)?(\d)*)*\d$/)); + } + + if (element.mandatory) { + this.contactForm.filter(contact => contact.id === element.identifier)[0].required = true; + valArr.push(Validators.required); + } + + if (this.contactForm.filter(contact => contact.id === element.identifier)[0] !== undefined) { + this.contactForm.filter(contact => contact.id === element.identifier)[0].control.setValidators(valArr); + } + + }); + } + + initCustomElementForm(data: any) { + let valArr: ValidatorFn[] = []; + + let field: any = {}; + + data.customFields.forEach((element: any) => { + valArr = []; + + field = { + id: `customField_${element.id}`, + unit: 'complement', + label: element.label, + type: element.type, + control: new FormControl({ value: '', disabled: false }), + required: false, + display: false, + values: element.values.map((val: any) => { return { id: val, label: val } }) + }; + + if (element.type === 'integer') { + valArr.push(Validators.pattern(/^[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)$/)); + field.control.setValidators(valArr); + } + this.contactForm.push(field); + }); + } + + setContactData(data: any) { + let indexField = -1; + Object.keys(data).forEach(element => { + indexField = this.contactForm.map(field => field.id).indexOf(element); + if (!this.isEmptyValue(data[element]) && indexField > -1) { + this.contactForm[indexField].control.setValue(data[element]); + this.contactForm[indexField].display = true; + } + }); + this.checkFilling(); + } + + setContactCustomData(data: any) { + let indexField = -1; + Object.keys(data.customFields).forEach(element => { + indexField = this.contactForm.map(field => field.id).indexOf('customField_' + element); + if (!this.isEmptyValue(data[element]) && indexField > -1) { + this.contactForm[indexField].control.setValue(data.customFields[element]); + this.contactForm[indexField].display = true; + } + }); + this.checkFilling(); + } + + initBanSearch() { + this.http.get("../../rest/ban/availableDepartments").pipe( + tap((data: any) => { + if (data.default !== null && data.departments.indexOf(data.default.toString()) !== - 1) { + this.addressBANCurrentDepartment = data.default; + } + this.departmentList = data.departments; + }), + catchError((err: any) => { + this.notify.handleErrors(err); + return of(false); + }) + ).subscribe(); + } + + isValidForm() { + let state = true; + + this.contactForm.filter(contact => contact.display).forEach(element => { + if (element.control.status !== 'VALID') { + state = false; + } + element.control.markAsTouched() + }); + + return state; + } + + onSubmit() { + this.checkFilling(); + if (this.addressBANMode && this.emptyAddress()) { + this.notify.error('Choisissez une BAN'); + } else if (this.isValidForm()) { + if (this.contactId !== null) { + this.updateContact(); + } else { + this.createContact(); + } + } else { + this.notify.error('Veuillez corriger les erreurs'); + } + + } + + createContact() { + this.http.post("../../rest/contacts", this.formatContact()).pipe( + tap((data:any) => { + this.onSubmitEvent.emit(data.id); + this.notify.success(this.lang.contactAdded); + }), + //finalize(() => this.loading = false), + catchError((err: any) => { + this.notify.handleErrors(err); + return of(false); + }) + ).subscribe(); + } + + updateContact() { + this.http.put(`../../rest/contacts/${this.contactId}`, this.formatContact()).pipe( + tap(() => { + this.onSubmitEvent.emit(this.contactId); + this.notify.success(this.lang.contactUpdated); + }), + //finalize(() => this.loading = false), + catchError((err: any) => { + this.notify.handleErrors(err); + return of(false); + }) + ).subscribe(); + } + + formatContact() { + let contact: any = {}; + contact['customFields'] = {}; + const regex = /customField_[.]*/g; + + this.contactForm.filter(field => field.display).forEach(element => { + if (element.id.match(regex) !== null) { + contact['customFields'][element.id.split('_')[1]] = element.control.value; + } else { + contact[element.id] = element.control.value; + } + }); + return contact; + } + + isEmptyUnit(id: string) { + if (this.contactForm.filter(field => field.display && field.unit === id).length === 0) { + return true; + } else { + return false; + } + } + + initForm() { + this.contactForm.forEach(element => { + element.control = new FormControl({ value: '', disabled: false }); + }); + } + + toogleAllFieldsUnit(idUnit: string) { + this.contactForm.filter(field => field.unit === idUnit).forEach((element: any) => { + element.display = true; + }); + } + + noField(id: string) { + if (this.contactForm.filter(field => !field.display && field.unit === id).length === 0) { + return true; + } else { + return false; + } + } + + isEmptyValue(value: string) { + + if (value === null) { + return true; + + } else if (Array.isArray(value)) { + if (value.length > 0) { + return false; + } else { + return true; + } + } else if (String(value) !== '') { + return false; + } else { + return true; + } + } + + checkCompany(field: any) { + if (field.id === 'company' && (this.companyFound === null || this.companyFound.company !== field.control.value)) { + + this.http.get(`../../rest/autocomplete/contacts/company?search=${field.control.value}`).pipe( + tap(() => this.companyFound = null), + filter((data: any) => data.length > 0), + tap((data) => { + this.companyFound = data[0]; + }), + //finalize(() => this.loading = false), + catchError((err: any) => { + this.notify.handleErrors(err); + return of(false); + }) + ).subscribe(); + } + } + + setAddress(contact: any, disableBan: boolean = true) { + let indexField = -1; + Object.keys(contact).forEach(element => { + indexField = this.contactForm.map(field => field.id).indexOf(element); + if (!this.isEmptyValue(contact[element]) && indexField > -1 && ['company', 'addressNumber', 'addressStreet', 'addressAdditional2', 'addressPostcode', 'addressTown', 'addressCountry'].indexOf(element) > -1) { + this.contactForm[indexField].control.setValue(contact[element]); + this.contactForm[indexField].display = true; + } + }); + this.checkFilling(); + + this.addressBANMode = disableBan ? false : true; + } + + canDelete(field: any) { + if (field.id === "company") { + const lastname = this.contactForm.filter(contact => contact.id === 'lastname')[0]; + if (lastname.display && !this.isEmptyValue(lastname.control.value)) { + let valArr: ValidatorFn[] = []; + field.control.setValidators(valArr); + field.required = false; + return true; + } else { + let valArr: ValidatorFn[] = []; + valArr.push(Validators.required); + field.control.setValidators(valArr); + field.required = true; + return false; + } + } else if (field.id === "lastname") { + const company = this.contactForm.filter(contact => contact.id === 'company')[0]; + if (company.display && !this.isEmptyValue(company.control.value)) { + let valArr: ValidatorFn[] = []; + field.control.setValidators(valArr); + field.required = false; + return true; + } else { + let valArr: ValidatorFn[] = []; + valArr.push(Validators.required); + field.control.setValidators(valArr); + field.required = true; + return false; + } + } else if (field.required) { + return false; + } else { + return true; + } + } + + removeField(field: any) { + field.display = !field.display; + field.control.reset(); + this.checkFilling(); + } + + initAutocompleteAddressBan() { + this.addressBANInfo = this.lang.autocompleteInfo; + this.addressBANResult = []; + this.addressBANControl.valueChanges + .pipe( + debounceTime(300), + filter(value => value.length > 2), + distinctUntilChanged(), + tap(() => this.addressBANLoading = true), + switchMap((data: any) => this.http.get('../../rest/autocomplete/banAddresses', { params: { "address": data, 'department': this.addressBANCurrentDepartment } })), + tap((data: any) => { + if (data.length === 0) { + this.addressBANInfo = this.lang.noAvailableValue; + } else { + this.addressBANInfo = ''; + } + this.addressBANResult = data; + this.addressBANFilteredResult = of(this.addressBANResult); + this.addressBANLoading = false; + }) + ).subscribe(); + } + + resetAutocompleteAddressBan() { + this.addressBANResult = []; + this.addressBANInfo = this.lang.autocompleteInfo; + } + + selectAddressBan(ev: any) { + let contact = { + addressNumber: ev.option.value.number, + addressStreet: ev.option.value.afnorName, + addressPostcode: ev.option.value.postalCode, + addressTown: ev.option.value.city, + addressCountry: 'FRANCE' + }; + this.setAddress(contact, false); + this.addressBANControl.setValue(''); + } + + getValue(identifier: string) { + return this.contactForm.filter(contact => contact.id === identifier)[0].control.value; + } + + emptyAddress() { + if (this.contactForm.filter(contact => this.isEmptyValue(contact.control.value) && ['addressNumber', 'addressStreet', 'addressPostcode', 'addressTown', 'addressCountry'].indexOf(contact.id) > -1).length > 0) { + return true; + } else { + return false; + } + } + + goTo() { + const contact = { + addressNumber: this.contactForm.filter(contact => contact.id === 'addressNumber')[0].control.value, + addressStreet: this.contactForm.filter(contact => contact.id === 'addressStreet')[0].control.value, + addressPostcode: this.contactForm.filter(contact => contact.id === 'addressPostcode')[0].control.value, + addressTown: this.contactForm.filter(contact => contact.id === 'addressTown')[0].control.value, + addressCountry: this.contactForm.filter(contact => contact.id === 'addressCountry')[0].control.value + }; + window.open(`https://www.google.com/maps/search/${contact.addressNumber}+${contact.addressStreet},+${contact.addressPostcode}+${contact.addressTown},+${contact.addressCountry}`, '_blank') + } + + switchAddressMode() { + let valArr: ValidatorFn[] = []; + if (this.addressBANMode) { + + valArr.push(Validators.required); + + this.contactForm.filter(contact => ['addressNumber', 'addressStreet', 'addressPostcode', 'addressTown', 'addressCountry'].indexOf(contact.id) > -1).forEach((element: any) => { + if (element.mandatory) { + element.control.setValidators(valArr); + } + }); + this.addressBANMode = !this.addressBANMode; + } else { + this.contactForm.filter(contact => ['addressNumber', 'addressStreet', 'addressPostcode', 'addressTown', 'addressCountry'].indexOf(contact.id) > -1).forEach((element: any) => { + if (element.mandatory) { + element.control.setValidators(valArr); + } + }); + this.addressBANMode = !this.addressBANMode; + } + } + + getErrorMsg(error: any) { + if (error.required !== undefined) { + return this.lang.requiredField; + } else if (error.pattern !== undefined || error.email !== undefined) { + return this.lang.badFormat; + } else { + return 'unknow validator'; + } + } + + checkFilling() { + const countFilling = this.contactForm.filter(contact => contact.filling).length; + const countValNotEmpty = this.contactForm.filter(contact => !this.isEmptyValue(contact.control.value) && contact.filling).length; + + this.fillingRate.value = Math.round((countValNotEmpty * 100) / countFilling); + + if (this.fillingRate.value <= this.fillingParameters.first_threshold) { + this.fillingRate.class = 'warn'; + } else if (this.fillingRate.value <= this.fillingParameters.second_threshold) { + this.fillingRate.class = 'primary'; + } else { + this.fillingRate.class = 'accent'; + } + } +} diff --git a/src/frontend/app/app-common.module.ts b/src/frontend/app/app-common.module.ts index 3fcb0be54a81f5b544a173dff8492e649f6eb471..4b61ada4829dbfff905c4e93122e14594c7e5ade 100755 --- a/src/frontend/app/app-common.module.ts +++ b/src/frontend/app/app-common.module.ts @@ -60,6 +60,7 @@ import { FolderInputComponent } from '../app/folder/index import { TagInputComponent } from '../app/tag/indexing/tag-input.component'; import { DragDropDirective } from '../app/viewer/upload-file-dnd.directive'; import { ContactAutocompleteComponent } from './contact/autocomplete/contact-autocomplete.component'; +import { ContactsFormComponent } from './administration/contact/page/form/contacts-form.component'; import { DiffusionsListComponent } from './diffusions/diffusions-list.component'; @@ -120,7 +121,8 @@ export class MyHammerConfig extends HammerGestureConfig { DocumentViewerComponent, DragDropDirective, EcplOnlyofficeViewerComponent, - ContactAutocompleteComponent + ContactAutocompleteComponent, + ContactsFormComponent ], exports: [ CommonModule, @@ -161,7 +163,8 @@ export class MyHammerConfig extends HammerGestureConfig { DocumentViewerComponent, DragDropDirective, EcplOnlyofficeViewerComponent, - ContactAutocompleteComponent + ContactAutocompleteComponent, + ContactsFormComponent ], providers: [ HeaderService, diff --git a/src/frontend/app/app.module.ts b/src/frontend/app/app.module.ts index 39138fcc9318d97c4f82272f6cb4bf237ad46b04..73c6b1f41d7abadc42fd35b87914961b7f1af9cb 100755 --- a/src/frontend/app/app.module.ts +++ b/src/frontend/app/app.module.ts @@ -82,6 +82,7 @@ import { PrivilegeService } from '../service/privileges.service'; import { ActionsService } from './actions/actions.service'; import { ContactsListComponent } from './contact/list/contacts-list.component'; import { ContactsListModalComponent } from './contact/list/modal/contacts-list-modal.component'; +import { ContactModalComponent } from './administration/contact/modal/contact-modal.component'; @NgModule({ @@ -156,6 +157,7 @@ import { ContactsListModalComponent } from './contact/list/modal/contacts-list-m MailResumeComponent, ContactsListComponent, ContactsListModalComponent, + ContactModalComponent ], entryComponents: [ CustomSnackbarComponent, @@ -184,7 +186,8 @@ import { ContactsListModalComponent } from './contact/list/modal/contacts-list-m AttachmentPageComponent, AttachmentCreateComponent, AttachmentShowModalComponent, - ContactsListModalComponent + ContactsListModalComponent, + ContactModalComponent ], providers: [ FiltersListService, FoldersService, ActionsService, NotificationService, PrivilegeService ], bootstrap: [ AppComponent ] diff --git a/src/frontend/app/contact/autocomplete/contact-autocomplete.component.ts b/src/frontend/app/contact/autocomplete/contact-autocomplete.component.ts index ce13bd15d8a790ae85781a12defb068b2dc8b19e..0165766041558fad9c45706fd6f943fbb2582286 100755 --- a/src/frontend/app/contact/autocomplete/contact-autocomplete.component.ts +++ b/src/frontend/app/contact/autocomplete/contact-autocomplete.component.ts @@ -11,6 +11,7 @@ import { Observable, of } from 'rxjs'; import { debounceTime, filter, distinctUntilChanged, tap, switchMap, exhaustMap, catchError, finalize } from 'rxjs/operators'; import { LatinisePipe } from 'ngx-pipes'; import { PrivilegeService } from '../../../service/privileges.service'; +import { ContactModalComponent } from '../../administration/contact/modal/contact-modal.component'; @Component({ selector: 'app-contact-autocomplete', @@ -219,19 +220,14 @@ export class ContactAutocompleteComponent implements OnInit { } } - addItem() { - const newElem = {}; + openContact() { + const dialogRef = this.dialog.open(ContactModalComponent, { width : '1200px', maxWidth : '100vw', data: { } }); - newElem[this.key] = this.myControl.value; - - this.http.post('../../rest/tags', { label: newElem[this.key] }).pipe( - tap((data: any) => { - for (var key in data) { - newElem['id'] = data[key]; - this.newIds.push(data[key]); - } - this.setFormValue(newElem); - this.myControl.setValue(''); + dialogRef.afterClosed().pipe( + //filter((data: string) => data === 'ok'), + tap((data) => { + console.log(data); + }), catchError((err: any) => { this.notify.handleErrors(err);