import { ChangeDetectorRef, Component, inject, OnDestroy, OnInit, signal, ViewChild } from '@angular/core'; import { Router, ActivatedRoute } from '@angular/router'; import {DialogService, DynamicDialogRef} from 'primeng/dynamicdialog'; import {DynamicDialogConfig} from 'primeng/dynamicdialog'; import { FormControl, ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms'; import { Subject, Subscription} from 'rxjs'; import { ConfirmationService, MessageService } from 'primeng/api'; import { DatePickerModule } from 'primeng/datepicker'; import { Code, RelationShipView, Person, mState, RelationShip, PersonPhotoDto} from '../models'; import { AppSettingService, LookupService, Utils } from '../shares'; import { PersonService } from './person.service'; import { ButtonModule } from 'primeng/button'; import { SelectModule } from 'primeng/select'; import { CheckboxModule } from 'primeng/checkbox'; import { InputTextModule } from 'primeng/inputtext'; import moment from 'moment'; import { HttpEvent } from '@angular/common/http'; import { TableModule } from 'primeng/table'; import { Pickperson } from '../pickperson/pickperson'; import { ImageDisplayComponent } from '../pickperson/image.display'; import { TooltipModule } from 'primeng/tooltip'; import { AutoFocusModule } from 'primeng/autofocus'; import { DomSanitizer } from '@angular/platform-browser'; import { PhotoList } from '../attachphoto/photolist'; export interface FileUploadEvent { originalEvent: HttpEvent; files: File[]; }; export interface FileUploadHandlerEvent { files: File[]; }; @Component({ templateUrl: 'person.edit.html', selector: 'person-edit', imports:[ButtonModule,TableModule,ReactiveFormsModule,TooltipModule, AutoFocusModule, SelectModule,CheckboxModule, InputTextModule,DatePickerModule], styleUrls: ['person.edit.css'], providers: [DialogService] }) export class PersonEdit implements OnInit, OnDestroy { editRef: DynamicDialogRef | undefined; private sanitizer = inject(DomSanitizer); imageDataUrl = signal(""); returnUrl =''; loginUser =''; hostsite =''; _error =''; _id= -1; _partnerId = -1; fatherList: Code[] =[]; motherList: Code[] =[]; sexList:Code[] =[]; familyList: Person[] =[]; profileFile: File | null = null; photoList: PersonPhotoDto[] = []; fileName = ''; isNew = false; validationPoints?: Code[]; partners = signal([]); deletePartner: RelationShipView[] =[]; msg="[Family Edit Component] "; private formBuilder = inject(UntypedFormBuilder); private personService = inject(PersonService); private messageService = inject(MessageService); private confirmationService = inject(ConfirmationService); //for focus input // @ViewChild('mystaffid') mystaffNo!: MatInput; isChange = true; // disable use false//true for not disable. make sure it true is disable button. private subscription:Subscription = new Subscription(); subChanged$ = new Subject(); adminuserForm = this.formBuilder.group({ id: [0], //Validators.required email: [''], //Validators.required firstname: ['',Validators.required], //Validators.required lastname: ['',Validators.required], //Validators.required phone: [''], //Validators.required address: [''], sex:['M',Validators.required], image: [''], dob: new FormControl(null), alive: [false], //Validators.required fatherId:[0], motherId:[0] }); constructor( private cdr: ChangeDetectorRef,public dialogService: DialogService, public ref: DynamicDialogRef, public config: DynamicDialogConfig, private router: Router, private route: ActivatedRoute, private appSetting: AppSettingService, ) { this.hostsite = this.appSetting.appSetting.attachment; } loadParentList( list: Person[], selfId: number): void { this.familyList = list; let i = 0; let item: Code; let family: Person; //const list = this.familyService.parentList; for (i = 0; i < list.length; i++) { family= list[i]; if (family.id != selfId) { item = { id: family.id, name: family.firstName! + " " + family.lastName!, status: family.lastName!, active: family.alive! }; if (family.sex == 'F') this.motherList.push(item); else this.fatherList.push(item); } } } getClassForRequire(prev:string,name:string){ const notok = !this.adminuserForm.controls[name].valid && this.adminuserForm.controls[name].touched; let str =prev; if (notok) str += " ng-invalid ng-dirty"; return str; } dislayImage(): any { if (this.profileFile) { return URL.createObjectURL(this.profileFile); } else if (this.imageDataUrl() != "") { return this.imageDataUrl(); } return null; } onFileSelected(event: any) { const file: File = event.target.files[0]; if (file) { this.profileFile = file; //this.messageService.add({ severity: 'info', summary: 'Success', detail: 'File Uploaded!' }); this.adminuserForm.patchValue({ image: this.profileFile.name }); this.fileName = file.name; this.subChanged$.next(false); } } deleteFile(): void { this.confirmationService.confirm({ message: 'Are you sure that you want to delete image?', header: 'Confirmation', closable: true, closeOnEscape: true, icon: 'pi pi-exclamation-triangle', rejectButtonProps: { label: 'Cancel', severity: 'secondary', outlined: true, }, acceptButtonProps: { label: 'Delete', }, accept: () => { this.doDeleteFile(); }, }); } doDeleteFile(): void { const fileName = this.adminuserForm.value.image; if (fileName) { this.doDeleteImage(fileName); } else if (this.profileFile) this.profileFile = null; } doDeleteImage(fileName:string) : void { const familyId = this.adminuserForm.value.id; const data = { fileName, familyId }; const delete$ = this.personService.deleteUploadFile(data); this.subscription.add(delete$.subscribe( { next: x => { if (x.statusCode == 1) { { this.adminuserForm.patchValue({ image: "" }); this.cdr.detectChanges(); this.messageService.add({ severity: 'success', summary: 'Delete image all ok', detail: 'file: ' + fileName }); } } else { this.messageService.add({ severity: 'error', summary: 'Error delete upload file', detail: 'Fail to delete upload file: ' + x.message }); } }, error: e => { this.messageService.add({ severity: 'error', summary: 'Error delete upload file', detail: 'Fail to delete upload file: ' + e }); } } )); } getFileToSave(file: File): FormData { //const familyId = this.adminuserForm.value.id!; const formData = new FormData(); formData.append("file", file); return formData; } // formData.append("familyId", familyId.toString()); // console.log("upload file", familyId, file); saveFile(formData: FormData) { const upload$ = this.personService.uploadFile(formData); this.subscription.add(upload$.subscribe( { next: x => { if (x.statusCode == 1) { const filename = x.data; //this.messageService.add({ severity: 'info', summary: 'Success', detail: 'File Uploaded to server API: ' + x.data }); this.adminuserForm.patchValue({ image: filename }); this.cdr.detectChanges(); } else this.messageService.add({ severity: 'error', summary: 'Error upload file', detail: 'Fail to upload file: ' + x.message }); }, error: e => this.messageService.add({ severity: 'error', summary: 'Error upload file', detail: 'Fail to upload file: ' }) } )); } populateSex(): void { let item: Code; item = { id:1, name:"Female", status:'F' }; this.sexList.push(item); item = { id:2, name:"Male", status:'M' }; this.sexList.push(item); } ngOnInit():void { this.populateSex(); const id = this.config.data.id; console.log("family edit", this.config); this.loadParentList(this.config.data.familyList, id); //this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/'; // const id = Number(this.route.snapshot.paramMap.get('id')); // now load thing up const user = Utils.getCurrentUser(); // console.log(this.msg + "current login user ", user); if (user.username === '') alert("you are not login."); else this.loginUser = user.firstName; this._id = id; //console.log(this.msg + " " + id ); this.subscription.add(this.adminuserForm.valueChanges.subscribe(x => this.isChange = false)); this.subscription.add(this.subChanged$.subscribe(x => this.isChange = x)); if (id > 0) { this.isNew = false; this.subscription.add( this.personService.loadPersonById(id).subscribe({ next: x => this.assignValue(x.data), error: e => console.log(e) }) ); } else { this.isNew = true; } } // this for disable submit button get isFieldsChange() { const chan = this.isChange || !this.adminuserForm.valid; // this disable so need true valid = true not console.log(this.msg + 'is fields change', chan); return chan; } doViewImage(imageName:string): void { const ref = this.dialogService.open(ImageDisplayComponent, { data: { imageName, }, header: 'View Image', draggable: true, width: '70%', modal:true, maximizable: true }); ref.onClose.subscribe((item: Person) => { if (item) { //console.log("after close ward edit", item); this.messageService.add({severity:'success', summary: 'Select', detail: item.firstName!}); //update the current list } }); } assignValue(item:Person): void { this._id = item.id; this.fileName = item.image!; if (item.relationShips) this.populatePartner(item.relationShips, item.id); if (item.personPhotos) { this.photoList = item.personPhotos; } this.adminuserForm.patchValue({ id: item.id, email: item.email, firstname: item.firstName, lastname: item.lastName, alive: item.alive, phone: item.phone, address: item.address, fatherId: item.fatherId, motherId: item.motherId, sex: item.sex, image: item.image }); const dob = item.dob; if (dob) { this.adminuserForm.patchValue({ dob: moment(dob).toDate() }); } if (item.image && item.image.length > 0) { this.loadImage(item.image); } // disable use false//true for not disable. this.subChanged$.next(true); } // convenience getter for easy access in form fields get f() { return this.adminuserForm.controls;} loadImage(fileName: string| null): void { const download = this.personService.downloadFile(fileName!); this.subscription.add(download.subscribe({ next: x => { if (x.statusCode == 1) { this.displayIamge64(x.data); } else { console.log("error in download in api ", x.message); } }, error: e => console.error("error in download image", e) })); } displayIamge64(baseImage: string): void { const changeUrl = (this.sanitizer.bypassSecurityTrustResourceUrl(baseImage) as any).changingThisBreaksApplicationSecurity; console.log('this is bypassSecurityTrustResourceUrl', changeUrl); const fullDataUri = `data:image/png;base64,${changeUrl}`; this.imageDataUrl.set(fullDataUri); } validate(adminuser:Person): boolean { let result = true; console.log("validate", adminuser); if (!adminuser.firstName) { this._error = 'firstname is blank or empty'; result = false; } else if (adminuser.lastName == '') { this._error = 'lastname is blank or empty'; result = false; } return result; } getPartnerForSave(): RelationShip[] { let rlist: RelationShip[]= []; let i =0; let vitem: RelationShipView; let item: RelationShip; for (i = 0; i < this.deletePartner.length; i++) { vitem = this.deletePartner[i]; item = { id: vitem.id, personId: vitem.personId, state: mState.Delete, relatePersonId: vitem.relatePersonId }; rlist.push(item); console.log("get partner for save delete list ", vitem); } console.log("get partner for save before loop this.partners ", i); for (i = 0; i < this.partners().length; i++) { vitem = this.partners()[i]; if (vitem.state == mState.New || vitem.state == mState.Modified) { item = { id: vitem.id, personId: vitem.personId, state: vitem.state, relatePersonId: vitem.relatePersonId }; rlist.push(item); } console.log("get partner for save in partner list ", vitem); } return rlist; } async onSubmit(e:Event): Promise { e.preventDefault(); // if form valid then go save //make sure const okToSave = this.adminuserForm.valid; if (okToSave) { let adminuserValue = this.adminuserForm.value; let item:Person = { id: adminuserValue.id, email: adminuserValue.email, firstName: adminuserValue.firstname, lastName: adminuserValue.lastname, alive: adminuserValue.alive, phone: adminuserValue.phone, address: adminuserValue.address, image:adminuserValue.image, fatherId: adminuserValue.fatherId, motherId: adminuserValue.motherId, sex: adminuserValue.sex }; if (adminuserValue.dob) { item.dob = moment(adminuserValue.dob).format("YYYY-MM-DD"); } this._error =''; const allOK = this.validate(item); if (allOK) { let container:any = { person: item }; if (this.profileFile != null) { container.formData = await Utils.toBase64(this.profileFile); container.fileName = this.profileFile.name; console.log('image as base64 ', container); } container.person.relationShips = this.getPartnerForSave(); this.subscription.add ( this.personService.savePerson(container).subscribe({ next: x => { if (x.statusCode >= 1) { item.id = x.data; // this.messageService.add({severity:'success', summary: 'Save user', detail: item.firstName + " " + item.lastName }); //this.router.navigate([this.returnUrl]); console.log("the person after save", item); const list = this.getPhotoListforSave(); if (list.length > 0) { this.savePhotoList(list, item); } else this.ref.close(item); } else { this.messageService.add({severity:'error', summary: 'Error', detail: x.message }); } }, error: e => { const message = e.message; this.messageService.add({severity:'error', summary: 'Error', detail: message }); console.error("error ", e); } }) ); } else this.messageService.add({severity:'error', summary: 'Error', detail: this._error }); } } getFamilyById(id: number): Person | undefined { let result:Person |undefined; result = this.familyList.find(x => x.id == id); return result; } populatePartner(list: RelationShip[], personId: number): void { let item: RelationShip; let vitem: RelationShipView; let vlist: RelationShipView[] =[]; let i = 0; let family:Person|undefined; for (i = 0; i < list.length; i++) { item = list[i]; if (item.relatePersonId != personId) { family = this.getFamilyById(item.relatePersonId); } else { family = this.getFamilyById(item.personId); } if (family) { vitem = { id: item.id, personId: item.personId, relatePersonId: item.relatePersonId, pfirstName: family.firstName, plastName: family.lastName, sex: family.sex, state:mState.NoChange }; vlist.push(vitem); } } if (vlist.length > 0) this.partners.set(vlist); } viewAttachment(): void { this.showAttachment("Add family photo or other"); } addPartner(): void { const title = "Partner"; this.showPickPerson(title, (sfamily: Person) => { let item: RelationShipView = { id: this._partnerId--, state: mState.New, personId: this._id, relatePersonId: sfamily.id, pfirstName: sfamily.firstName, plastName: sfamily.lastName, sex: sfamily.sex, }; const fm = this.partners(); fm.push(item); this.partners.set(fm); this.subChanged$.next(false); console.log("after put in partners list", this.partners()); }); } onDeletePartner(item:RelationShipView): void { this.confirmationService.confirm({ message: 'Are you sure that you want to delete partner?', header: 'Confirmation', closable: true, closeOnEscape: true, icon: 'pi pi-exclamation-triangle', rejectButtonProps: { label: 'Cancel', severity: 'secondary', outlined: true, }, acceptButtonProps: { label: 'Delete', }, accept: () => { this.doDeletePartner(item); }, }); } doDeletePartner(item:RelationShipView): void { if (item.id > 0) { this.deletePartner.push(item); } const dlist = this.partners().filter(x => x.id != item.id); this.partners.set(dlist); console.log("after delete partners list", this.partners()); this.subChanged$.next(false); } showPickPerson(title:string, callback:(id: Person) => void) :void { const ref = this.dialogService.open(Pickperson, { data: { familyList: this.familyList, }, header: title, width: '80%', draggable: true, maximizable: true }); ref.onClose.subscribe((item: Person) => { if (item) { //console.log("after close ward edit", item); this.messageService.add({severity:'success', summary: 'Select', detail: item.firstName!}); //update the current list callback(item); } }); } showAttachment(title:string): void { const ref = this.dialogService.open(PhotoList, { data: { id: this._id, personPhotos: this.photoList }, header: title, width: '80%', draggable: true, maximizable: true }); ref.onClose.subscribe((ritem: any) => { const item = ritem.list; const deleteIds = ritem.deleteIds; if (item) { console.log("after close " + title, item); this.messageService.add({severity:'success', summary: title, detail: "photo attachment " + item.length}); //update the current list // callback(item); this.updatePhotoList(item); } if (deleteIds && deleteIds.length > 0) { let j = 0; for (let i = 0; i < deleteIds.length; i++) { const id = deleteIds[i]; j = this.photoList.findIndex(x => x.id == id); if (j > -1) { this.photoList.splice(j,1); } } this.cdr.markForCheck(); } }); } updatePhotoList(list: PersonPhotoDto[]): void { for (let i = 0; i < list.length; i++) { const item = list[i]; const idx = this.photoList.findIndex(x => x.id == item.id); if (idx && idx < 0) this.photoList.push(item); else { let eitem = this.photoList[idx]; eitem.photo = item.photo; eitem.photoType = item.photoType; eitem.file = item.file; } } console.log("the photos after close", this.photoList); this.subChanged$.next(false);//make save button to enable. this.cdr.markForCheck(); } getPhotoListforSave(): PersonPhotoDto[] { let result: PersonPhotoDto[] =[]; for (let i = 0; i < this.photoList.length; i++) { if (this.photoList[i].id < 0) { result.push(this.photoList[i]); } } return result; } savePhotoList(list: PersonPhotoDto[], item:Person): void { const formData = new FormData(); for (let i = 0; i < list.length; i++) { const file = list[i].file; if (file) formData.append("files", file, file.name); } formData.append('personId', this._id.toString()); const personPhoto$ = this.personService.savePersonPhotoList(formData); this.subscription.add(personPhoto$.subscribe( { next: x => { if (x.statusCode == 1) { //const filename = x.data; //this.adminuserForm.patchValue({ image: filename }); this.ref.close(item); //this.cdr.detectChanges(); } else this.messageService.add({ severity: 'error', summary: 'Error save attach photos files', detail: 'Fail to photos file: ' + x.message }); }, error: e => this.messageService.add({ severity: 'error', summary: 'Error attach photos file', detail: 'Fail to photos file: ' }) } )); } cancel(e:Event):void { e.preventDefault(); this.ref.close(null); } ngOnDestroy() { this.subscription.unsubscribe(); } }