import { AfterContentChecked, AfterViewInit, ChangeDetectorRef, Component, DoCheck, EventEmitter, HostListener, Input, KeyValueChanges, KeyValueDiffer, KeyValueDiffers, NgZone, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, ViewChild, ViewEncapsulation } from '@angular/core'; import {FormlyFieldConfig, FormlyFormOptions} from '@ngx-formly/core'; import {FormArray, FormGroup} from '@angular/forms'; import {AllService} from './all.service'; import { faCheck, faStepForward, faFolderOpen, faCreditCard, faBuilding, faPenNib, faMedkit, faUser, faUsers, faSignOutAlt, faDownload, faHome, faCircle } from '@fortawesome/free-solid-svg-icons'; import {MatStepper} from '@angular/material/stepper'; import {Observable, Subscription} from 'rxjs'; import {DeviceDetectorService} from 'ngx-device-detector'; import {NgbDateAdapter, NgbDateParserFormatter} from '@ng-bootstrap/ng-bootstrap'; import {CustomAdapter, CustomDateParserFormatter} from './components-formly/shared/datepicker/datepicker.component'; export interface StepType { label: string; fields: FormlyFieldConfig[]; } @Component({ selector: 'jhi-preview-magnolia', templateUrl: './preview-magnolia.component.html', styleUrls: ['./preview-magnolia.component.scss'], providers: [{provide: NgbDateAdapter, useClass: CustomAdapter}, {provide: NgbDateParserFormatter, useClass: CustomDateParserFormatter}], encapsulation: ViewEncapsulation.None }) export class PreviewMagnoliaComponent implements OnInit, OnDestroy, DoCheck, OnChanges, AfterContentChecked, AfterViewInit { faCheck = faCheck; faStepForward = faStepForward; faFolderOpen = faFolderOpen; faCreditCard = faCreditCard; faBuilding = faBuilding; faPenNib = faPenNib; faMedkit = faMedkit; faUser = faUser; faSignOutAlt = faSignOutAlt; faDownload = faDownload; faUsers = faUsers; faHome = faHome; faCircle = faCircle; @Input() steps: StepType[] = []; @Input() model: any = {}; @Input() services: any = {}; @Input() activedStep = 0; @Input() hasMedical = false; @Input() product: any = {}; @Input() eventsDetectStep: Observable; @Input() configurationPreview: any = {}; @Input() contract: any = {}; @Input() loadPositionStep = false; private eventsSubscription: Subscription; subscriptions = []; @Output() saveDataStep: EventEmitter = new EventEmitter(); @Output() updateVariables: EventEmitter = new EventEmitter(); @Output() activedStepStatus: EventEmitter = new EventEmitter(); @Output() saveAnythingFromStepBuiltIn: EventEmitter = new EventEmitter(); @Output() setOriginalVariableValue: EventEmitter = new EventEmitter(); @ViewChild('stepper') stepper: MatStepper; modelDiffer: any; activedStepDiffer: any; options: FormlyFormOptions = {}; fields: FormlyFieldConfig[] = []; form = new FormArray(this.steps.map(() => new FormGroup({}))); modelReady = false; stepIcons = []; stepVisible = []; icons = { 'faCheck': faCheck, 'faStepForward': faStepForward, 'faFolderOpen': faFolderOpen, 'faCreditCard': faCreditCard, 'faBuilding': faBuilding, 'faPenNib': faPenNib, 'faMedkit': faMedkit, 'faUser': faUser, 'faSignOutAlt': faSignOutAlt, 'faDownload': faDownload, 'faUsers': faUsers, 'faHome': faHome, 'faCircle': faCircle, }; private stepsDiffer: Array> = []; private stepSignatureActif = false; disabledTransition = false; deviceInfo = null; currentStepVisible = null; showButtonNext = true; showButtonPrevious = true; allDataReadyToRenderForm = false; @HostListener('window:beforeunload', ['$event']) detectIframeLoaded($event) { if (this.stepSignatureActif) { return false; } else { return true; } } constructor( private differs: KeyValueDiffers, public allService: AllService, private cdref: ChangeDetectorRef, private ngZone: NgZone, private deviceService: DeviceDetectorService ) { } ngOnInit(): void { console.log('%c PREVIEW VERSION : 0.5.95', 'background: #222; color: #5cc700; font-size: 2em'); this.allService.configurationPreview = this.configurationPreview; this.allService.faIcons = this.icons; document.documentElement.style.setProperty('--first-color', this.configurationPreview.buttonColor); document.documentElement.style.setProperty('--text-color', this.configurationPreview.textColor); document.documentElement.style.setProperty('--theme-color', this.configurationPreview.themeColor); // Set des couleurs pour la décision médicale document.documentElement.style.setProperty('--background-color-selected-text', this.configurationPreview.stepMedicalDecision.backgroundColorSelectedAndText); document.documentElement.style.setProperty('--price-taux-color', this.configurationPreview.stepMedicalDecision.priceTauxColor); document.documentElement.style.setProperty('--text-header-background-color', this.configurationPreview.stepSummary.textHeaderBackgroundColor); // Set des couleurs pour les boutons document.documentElement.style.setProperty('--cta', this.configurationPreview.cta); document.documentElement.style.setProperty('--cta-hover', this.configurationPreview.ctaHover); document.documentElement.style.setProperty('--cta-disable', this.configurationPreview.ctaDisable); this.eventsSubscription = this.eventsDetectStep.subscribe((info) => { this.detectStep(info); }); this.allService.saveDataStep = this.saveDataStep; this.allService.product = this.product; this.steps.forEach((step) => { this.stepsDiffer.push(this.differs.find(step).create()); }); this.setServices(); this.allService.contract = this.contract; this.allService.productIdOriginal = this.contract.productId; this.mapFields(); this.form = new FormArray(this.steps.map(() => new FormGroup({}))); this.modelDiffer = this.differs.find(this.model).create(); this.setIconsAndStepVisible(); const subs = this.allService.getStepsSubjectAsObserVable().subscribe((data) => { if (data === 'next') { this.nextStep(this.activedStep, true); } }); this.subscriptions.push(subs); this.detectDevice(); this.allService.saveAnythingFromStepBuiltIn = this.saveAnythingFromStepBuiltIn; this.allService.setOriginalVariableValue = this.setOriginalVariableValue; this.form.controls[6].statusChanges.subscribe(status => { console.log('New status', status); }); } private setServices() { for (const service in this.services) { if (this.services.hasOwnProperty(service)) { this.allService[service] = this.services[service]; } } } ngAfterContentChecked() { this.cdref.detectChanges(); } ngAfterViewInit(): void { if (this.contract.currentStepId && this.loadPositionStep && !this.effectiveDateBeforeToday()) { setTimeout(() => { this.setActivatedStepById(this.contract.currentStepId); this.setCompletedStep(); this.goToLastStep(); }, 250); } else { this.allDataReadyToRenderForm = true; } this.showOrHideButtonNext(this.stepVisible[this.activedStep]); this.showOrHideButtonPrevious(this.stepVisible[this.activedStep]); } ngOnDestroy(): void { this.eventsSubscription.unsubscribe(); this.subscriptions.forEach(subs => subs.unsubscribe()); } setIconsAndStepVisible(): void { this.stepIcons = []; this.stepVisible = []; this.steps.forEach((step: any, index: number) => { if (!step.hide) { this.stepVisible.push(step); this.stepIcons.push(step.icon); } this.allService.stepIcons = this.stepIcons; }); this.currentStepVisible = this.stepVisible[this.activedStep]; } mapFields(): void { this.steps.forEach((step: any) => { if (step.hideExpression) { step.hide = eval(step.hideExpression); } // Set the data for stepSummary if (step.type === 'stepSummary') { const field = this.getField('summary', step.fields); field.templateOptions.options = this.model; } step.fields.forEach((field: any) => { return field.fieldGroup.map((f: any) => { if (f.fieldGroup) { f.fieldGroup.forEach(element => { if (element.variable) { this.setValue(element); } this.setLabelVariable(element); if (element.templateOptions && element.templateOptions.change && typeof element.templateOptions.change === 'string') { let changeFunc = element.templateOptions.change.toString(); changeFunc = eval(changeFunc); element.templateOptions.change = (field) => { changeFunc(field); this.updateVariables.emit(this.model); }; } this.setValidators(element); element.hooks = { onInit: (field: any) => { if (field.type !== 'mandatoryField') { field.formControl.setValue(this.model[field.variable]); } } }; return element; }); } else { if (f.variable) { this.setValue(f); } this.setLabelVariable(f); if (f.templateOptions && f.templateOptions.change && typeof f.templateOptions.change === 'string') { let changeFunc = f.templateOptions.change.toString(); changeFunc = eval(changeFunc); f.templateOptions.change = (field) => { changeFunc(field); this.updateVariables.emit(this.model); }; } return f; } }); }); }); } setValidators(element): void { const validators = ['zipcode', 'mandatory']; const keyExist = ''; validators.forEach((key) => { if (element.validators && element.validators.hasOwnProperty(key)) { if (typeof element.validators[key].expression === 'string') { let expressionFunc = element.validators[key].expression.toString(); expressionFunc = eval(expressionFunc); element.validators[key].expression = (field) => { return expressionFunc(field); }; if (element.validators[key].message) { let messageFunc = element.validators[key].message.toString(); messageFunc = eval(messageFunc); element.validators[key].message = (error, field) => { return messageFunc(error, field); }; } } } }); } setLabelVariable(f) { /* const regex = /\{\{(.*?)}\}/g; const resultV = regex.exec(f.templateOptions.label); if (resultV && resultV.length === 2) { f.templateOptions.label = f.templateOptions.label.replace(regex, this.model[resultV[1]]); }*/ } setValue(f) { if (this.model[f.variable] === 'true') { this.model[f.key] = true; } else if (this.model[f.variable] === 'false') { this.model[f.key] = false; } else { this.model[f.key] = this.model[f.variable]; } } prevStep(step: number): void { this.activedStep = this.activedStep - 1; this.setContractCurrentStage(); this.setCompletedStep(); this.setIconsAndStepVisible(); this.ngZone.run(() => { this.stepper.previous(); this.allService.currentStepCustomError = false; this.allService.activeStep = this.stepVisible[this.activedStep]; this.showOrHideButtonNext(this.stepVisible[this.activedStep]); this.showOrHideButtonPrevious(this.stepVisible[this.activedStep]); this.updateVariables.emit(this.model); this.allService.stepsSubject.next({ step: this.stepVisible[this.activedStep], index: this.activedStep }); this.currentStepVisible = this.stepVisible[this.activedStep]; window.document.getElementsByClassName('container-fluid')[0].scrollIntoView(); }); } nextStep(step: number, force?: boolean): boolean { if (!this.form.at(step).valid) { this.showErrorsOfCurrentStep(step); return false; } if (this.allService.currentStepCustomError) { this.allService.submitedStepSubject.next(true); return false; } if (this.steps[this.activedStep].fields[0].fieldGroup[0].type !== 'stepSummary' && this.steps[this.activedStep].fields[0].fieldGroup[0].type !== 'stepSummaryFuneral' || force) { this.activedStep = this.activedStep + 1; this.setIconsAndStepVisible(); this.showOrHideButtonNext(this.stepVisible[this.activedStep]); this.showOrHideButtonPrevious(this.stepVisible[this.activedStep]); this.setContractCurrentStage(); this.setCompletedStep(); this.updateVariables.emit(this.model); this.ngZone.run(() => { this.stepper.next(); this.allService.currentStepCustomError = false; this.allService.activeStep = this.stepVisible[this.activedStep]; this.allService.stepsSubject.next({ step: this.stepVisible[this.activedStep], index: this.activedStep }); this.currentStepVisible = this.stepVisible[this.activedStep]; const previewMagnolia = window.document.getElementsByClassName('component-preview-magnolia')[0]; previewMagnolia.scrollIntoView(); const y = previewMagnolia.getBoundingClientRect().top + window.pageYOffset - 80; window.scrollTo({top: y, behavior: 'smooth'}); }); } if (this.steps[this.activedStep].fields[0].fieldGroup[0].type === 'stepSummary' && !force || this.steps[this.activedStep].fields[0].fieldGroup[0].type === 'stepSummaryFuneral' && !force) { this.allService.showModalDocumentFromSummarySubject.next(true); return false; } return true; } modelChanged(): void { this.steps.forEach((step: any) => { if (step.hideExpression) { step.hide = eval(step.hideExpression); } }); this.setIconsAndStepVisible(); } ngDoCheck(): void { if (this.modelDiffer) { const changes = this.modelDiffer.diff(this.model); if (changes != null) { this.modelChanged(); } } this.stepsDiffer.forEach((stepDiffer, index) => { const changes = stepDiffer.diff(this.steps[index]); if (changes) { this.setIconsAndStepVisible(); } }); this.allService.hasMedical = this.hasMedical; /* if (this.activedStepDiffer) { console.log('changement de step'); } */ } ngOnChanges(changes: SimpleChanges) { // console.log(changes.activedStep); // console.log(this.steps.length); // // You can also use categoryId.previousValue and // categoryId.firstChange for comparing old and new values } detectStep(error?) { this.stepSignatureActif = false; const step = this.stepVisible[this.stepper.selectedIndex]; const stepToLaunchSignature = ['stepSignature', 'stepSignatureBpa']; if (step && stepToLaunchSignature.includes(step.fields[0].fieldGroup[0].type)) { error = error ? error : ''; this.generateSignature(error); } //this.allService.currentStepCustomError = false; //this.allService.stepsSubject.next(step); } getField(key: string, fields: FormlyFieldConfig[]): FormlyFieldConfig { for (let i = 0, len = fields.length; i < len; i++) { const f = fields[i]; if (f.key === key) { return f; } if (f.fieldGroup && !f.key) { const cf = this.getField(key, f.fieldGroup); if (cf) { return cf; } } } return null; } generateSignature(error) { this.stepSignatureActif = !error; const info = error ? error : 'signer'; this.allService.signatureSubject.next(info); } showErrorsOfCurrentStep(step): void { const keyControls = 'controls'; let controlNameError = ''; // tslint:disable-next-line:forin for (const control in this.form.at(step)[keyControls]) { if (this.form.at(step)[keyControls][control].errors) { this.form.at(step)[keyControls][control].markAsTouched(); controlNameError = controlNameError ? controlNameError : control; } } const element = this.form.at(step).get(controlNameError); const currentForm = document.getElementById('form-' + step); const domField = currentForm.querySelectorAll('.ng-invalid')[0]; if (domField) { domField.scrollIntoView(); const y = domField.getBoundingClientRect().top + window.pageYOffset - 90; window.scrollTo({top: y, behavior: 'smooth'}); } } detectDevice(): void { this.deviceInfo = this.deviceService.getDeviceInfo(); if (this.deviceInfo.browser === 'Safari') { this.disabledTransition = true; } } showOrHideButtonNext(step): void { const arrayBtnHide = ['stepSignature', 'stepParterRe']; console.log('showOrHideButtonNext', step); const stepName = step.fields[0].fieldGroup[0].type; this.allService.showButtonNext = (arrayBtnHide.indexOf(stepName) === -1 && !step.hideButtonNext); } showOrHideButtonPrevious(step): void { const arrayBtnHide = ['stepParterRe']; console.log('showOrHideButtonPrevious', step); const stepName = step.fields[0].fieldGroup[0].type; this.allService.showButtonPrevious = (arrayBtnHide.indexOf(stepName) === -1 && !step.hideButtonPrevious); } goToLastStep(): void { this.stepper.selectedIndex = this.activedStep; this.allService.stepsSubject.next({ step: this.stepVisible[this.activedStep], index: this.activedStep }); this.currentStepVisible = this.stepVisible[this.activedStep]; this.allDataReadyToRenderForm = true; } setActivatedStepById(stepId: string) { this.stepVisible.find((item, i) => { if (item.productStepId === stepId) { this.activedStep = i; return true; } }); } setCompletedStep(): void { for (let i = 0; i <= this.activedStep; i++) { this.stepVisible[i].completed = true; } } setContractCurrentStage(): void { const indexSepa = this.stepVisible.findIndex(step => step.fields[0].fieldGroup[0].type === 'stepSepa'); this.model['contract_current_stage'] = (indexSepa > this.activedStep) ? 'MEDICAL' : 'SUBSCRIPTION'; } effectiveDateBeforeToday(): boolean { return new Date(new Date(this.model['insurance_effective_date']).toDateString()) <= new Date(new Date().toDateString()); } }