+
+
please click save at the end
+
+
+
+
+
+
+
+
+
+
+ | Id |
+ Description
+ |
+
+ Action |
+
+
+
+
+
+ | {{item.id}} |
+ {{item.photo}} |
+
+
+
+
+ @if (item.id > 0) {
+
+ }
+ |
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/UI/src/app/attachphoto/photolist.ts b/UI/src/app/attachphoto/photolist.ts
index f477939..b400d11 100644
--- a/UI/src/app/attachphoto/photolist.ts
+++ b/UI/src/app/attachphoto/photolist.ts
@@ -1,183 +1,183 @@
-//import { Component, OnInit, OnDestroy } from '@angular/core';
-import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal, output } from '@angular/core';
-import { catchError, EMPTY, finalize, Subscription } from 'rxjs';
-import { CommonUtilities, HttpUtility, LookupService } from '../shares';
-import { DialogService ,DynamicDialogConfig,DynamicDialogModule, DynamicDialogRef} from 'primeng/dynamicdialog';
-import { ConfirmationService, MessageService } from 'primeng/api';
-import { AddPhoto } from './add.photo';
-import { PersonPhotoDto } from '../models';
-
-import { CommonModule } from '@angular/common';
-import { FormsModule } from '@angular/forms';
-import { TableModule } from 'primeng/table';
-import { ButtonModule } from 'primeng/button';
-import { TooltipModule } from 'primeng/tooltip';
-import { AuthenticationService } from '../user-services';
-import { HttpResponse } from '@angular/common/http';
-import { PersonService } from '../person';
-
-@Component({
- selector: 'photo-list',
- imports: [CommonModule, FormsModule, DynamicDialogModule,TooltipModule,
- ButtonModule, TableModule],
- templateUrl: './photolist.html',
- styleUrls: ['./photolist.css'],
- providers: [DialogService]
-})
-export class PhotoList implements OnInit, OnDestroy {
- FileList: PersonPhotoDto[] =[]
- loading = false;
- _id = -1;
- deletePersonPhoto: number[] = [];
- private authenticationService = inject(AuthenticationService);
- private cdr = inject(ChangeDetectorRef);
- private confirmationService = inject(ConfirmationService);
- private messageService = inject(MessageService);
- private subscription: Subscription = new Subscription();
- downloadFile = signal(false);
- constructor(
- private http: HttpUtility,
- private personService: PersonService,
-
- public ref: DynamicDialogRef, public config: DynamicDialogConfig,
- public dialogService: DialogService
- ) { }
-
- ngOnInit(): void {
- const id = this.config.data.id;
- const olist = this.config.data.personPhotos;
- if (olist && olist.length > 0)
- {
- this.FileList = olist;
- this.assignFileId(olist);
- }
- }
- assignFileId(files: any[]): void {
- let i = 0;
- let id = -1;
- let mx = 1;
- for (i = 0; i < files.length; i++)
- {
- id = files[i].id;
- if (id < mx)
- {
- mx = id;
- }
- }
- this._id = -1 * mx;
- }
- downloadAttachment(id: number): void {
- //GetReportFile
-
- let typeofCall = "personId_" + id.toString();
-
- let criteria:any ={id, fileName:''};
- this.downloadFile.set(false);
- this.http.getFileResponse("api/FileUpload/downloadPersonPhoto", criteria).pipe(
- catchError(err => {
- console.error(err.message);
- return EMPTY;
- }),
- finalize(() => {
- // this.toastr.success("download completed ok to view now");
- this.downloadFile.set(false);
-
- })).subscribe((response: HttpResponse
) => {
- if (response) {
- console.log("the download report response", response);
- CommonUtilities.downloadFile(response, typeofCall);
- }
- });
- }
-
-onClose(event: Event): void {
- const nlist = this.FileList.filter( x => x.id < 1);
- this.ref.close({list: nlist, deleteIds: this.deletePersonPhoto});
-}
-newSupportDoc(event: Event): void {
- this.showEdit(this._id--);
-}
-remove(id: number): void {
- this.confirmationService.confirm({
-
- message: 'Do you want to delete this record?',
- header: 'Confirmation Delete',
- icon: 'pi pi-info-circle',
- rejectLabel: 'Cancel',
- rejectButtonProps: {
- label: 'Cancel',
- severity: 'secondary',
- outlined: true,
- },
- acceptButtonProps: {
- label: 'Delete',
- severity: 'danger',
- },
-
- accept: () => {
- this.doRemove(id);
- }
- });
-}
-doRemove(id: number): void {
- if (id < 0)
- {
- const nlist = this.FileList.filter(x => x.id != id);
- this.FileList = [...nlist];
- this.cdr.markForCheck();
- }
- else
- {
- const deletepersonPhoto$ = this.personService.deletePersonPhotoFile(id);
- this.subscription.add(deletepersonPhoto$.subscribe(
- {
- next: x => {
- if (x.statusCode == 1)
- {
- const nlist = this.FileList.filter(x => x.id != id);
- this.FileList = [...nlist];
- this.deletePersonPhoto.push(id);
- this.cdr.markForCheck();
- this.messageService.add({severity:'success', summary: 'delete person photo', detail: "person photo Id " + id });
- }
- },
- error:e => console.error("error in client delete person photo", e)
- }
- ));
- }
-
-
-
-}
- showEdit(id: number) {
- const ref = this.dialogService.open(AddPhoto, {
- data: {
- id,
- },
- header: 'Add File',
- width: '70%',
- maximizable: true,
- draggable: true
- });
- if (ref)
- {
- ref.onClose.subscribe((list: File[]) => {
- if (list) {
- //console.log("after close ward edit", item);
- this.messageService.add({ severity: 'success', summary: 'Attact File', detail: list.length.toString() });
- let it = id;
- for (let i = 0; i < list.length; i++)
- {
- const item = list[i];
- this.FileList.push({id: it--, photo: item.name, photoType:'', file: item});
- }
- this.cdr.markForCheck();
- }
- });
- }
- }
-
- ngOnDestroy() {
- this.subscription.unsubscribe();
- }
-}
+//import { Component, OnInit, OnDestroy } from '@angular/core';
+import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal, output } from '@angular/core';
+import { catchError, EMPTY, finalize, Subscription } from 'rxjs';
+import { CommonUtilities, HttpUtility, LookupService } from '../shares';
+import { DialogService ,DynamicDialogConfig,DynamicDialogModule, DynamicDialogRef} from 'primeng/dynamicdialog';
+import { ConfirmationService, MessageService } from 'primeng/api';
+import { AddPhoto } from './add.photo';
+import { PersonPhotoDto } from '../models';
+
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { TableModule } from 'primeng/table';
+import { ButtonModule } from 'primeng/button';
+import { TooltipModule } from 'primeng/tooltip';
+import { AuthenticationService } from '../user-services';
+import { HttpResponse } from '@angular/common/http';
+import { PersonService } from '../person';
+
+@Component({
+ selector: 'photo-list',
+ imports: [CommonModule, FormsModule, DynamicDialogModule,TooltipModule,
+ ButtonModule, TableModule],
+ templateUrl: './photolist.html',
+ styleUrls: ['./photolist.css'],
+ providers: [DialogService]
+})
+export class PhotoList implements OnInit, OnDestroy {
+ FileList: PersonPhotoDto[] =[]
+ loading = false;
+ _id = -1;
+ deletePersonPhoto: number[] = [];
+ private authenticationService = inject(AuthenticationService);
+ private cdr = inject(ChangeDetectorRef);
+ private confirmationService = inject(ConfirmationService);
+ private messageService = inject(MessageService);
+ private subscription: Subscription = new Subscription();
+ downloadFile = signal(false);
+ constructor(
+ private http: HttpUtility,
+ private personService: PersonService,
+
+ public ref: DynamicDialogRef, public config: DynamicDialogConfig,
+ public dialogService: DialogService
+ ) { }
+
+ ngOnInit(): void {
+ const id = this.config.data.id;
+ const olist = this.config.data.personPhotos;
+ if (olist && olist.length > 0)
+ {
+ this.FileList = olist;
+ this.assignFileId(olist);
+ }
+ }
+ assignFileId(files: any[]): void {
+ let i = 0;
+ let id = -1;
+ let mx = 1;
+ for (i = 0; i < files.length; i++)
+ {
+ id = files[i].id;
+ if (id < mx)
+ {
+ mx = id;
+ }
+ }
+ this._id = -1 * mx;
+ }
+ downloadAttachment(id: number): void {
+ //GetReportFile
+
+ let typeofCall = "personId_" + id.toString();
+
+ let criteria:any ={id, fileName:''};
+ this.downloadFile.set(false);
+ this.http.getFileResponse("api/FileUpload/downloadPersonPhoto", criteria).pipe(
+ catchError(err => {
+ console.error(err.message);
+ return EMPTY;
+ }),
+ finalize(() => {
+ // this.toastr.success("download completed ok to view now");
+ this.downloadFile.set(false);
+
+ })).subscribe((response: HttpResponse) => {
+ if (response) {
+ console.log("the download report response", response);
+ CommonUtilities.downloadFile(response, typeofCall);
+ }
+ });
+ }
+
+onClose(event: Event): void {
+ const nlist = this.FileList.filter( x => x.id < 1);
+ this.ref.close({list: nlist, deleteIds: this.deletePersonPhoto});
+}
+newSupportDoc(event: Event): void {
+ this.showEdit(this._id--);
+}
+remove(id: number): void {
+ this.confirmationService.confirm({
+
+ message: 'Do you want to delete this record?',
+ header: 'Confirmation Delete',
+ icon: 'pi pi-info-circle',
+ rejectLabel: 'Cancel',
+ rejectButtonProps: {
+ label: 'Cancel',
+ severity: 'secondary',
+ outlined: true,
+ },
+ acceptButtonProps: {
+ label: 'Delete',
+ severity: 'danger',
+ },
+
+ accept: () => {
+ this.doRemove(id);
+ }
+ });
+}
+doRemove(id: number): void {
+ if (id < 0)
+ {
+ const nlist = this.FileList.filter(x => x.id != id);
+ this.FileList = [...nlist];
+ this.cdr.markForCheck();
+ }
+ else
+ {
+ const deletepersonPhoto$ = this.personService.deletePersonPhotoFile(id);
+ this.subscription.add(deletepersonPhoto$.subscribe(
+ {
+ next: x => {
+ if (x.statusCode == 1)
+ {
+ const nlist = this.FileList.filter(x => x.id != id);
+ this.FileList = [...nlist];
+ this.deletePersonPhoto.push(id);
+ this.cdr.markForCheck();
+ this.messageService.add({severity:'success', summary: 'delete person photo', detail: "person photo Id " + id });
+ }
+ },
+ error:e => console.error("error in client delete person photo", e)
+ }
+ ));
+ }
+
+
+
+}
+ showEdit(id: number) {
+ const ref = this.dialogService.open(AddPhoto, {
+ data: {
+ id,
+ },
+ header: 'Add File',
+ width: '70%',
+ maximizable: true,
+ draggable: true
+ });
+ if (ref)
+ {
+ ref.onClose.subscribe((list: File[]) => {
+ if (list) {
+ //console.log("after close ward edit", item);
+ this.messageService.add({ severity: 'success', summary: 'Attact File', detail: list.length.toString() });
+ let it = id;
+ for (let i = 0; i < list.length; i++)
+ {
+ const item = list[i];
+ this.FileList.push({id: it--, photo: item.name, photoType:'', file: item});
+ }
+ this.cdr.markForCheck();
+ }
+ });
+ }
+ }
+
+ ngOnDestroy() {
+ this.subscription.unsubscribe();
+ }
+}
diff --git a/UI/src/app/import.com/import.com.html b/UI/src/app/import.com/import.com.html
index 36e7377..cfab7be 100644
--- a/UI/src/app/import.com/import.com.html
+++ b/UI/src/app/import.com/import.com.html
@@ -1,10 +1,10 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/UI/src/app/import.com/import.com.ts b/UI/src/app/import.com/import.com.ts
index 3f16541..c528b3c 100644
--- a/UI/src/app/import.com/import.com.ts
+++ b/UI/src/app/import.com/import.com.ts
@@ -1,38 +1,38 @@
-import { Component, inject, OnInit } from '@angular/core';
-import { ButtonModule } from 'primeng/button';
-import { FileUploadEvent, FileUploadModule } from 'primeng/fileupload';
-import { MessageService } from 'primeng/api';
-import { FileUpload } from 'primeng/fileupload';
-import { ToastModule } from 'primeng/toast';
-import { AppSettingService } from '../shares';
-/*
-interface UploadEvent {
- originalEvent: Event;
- files: File[];
-}*/
-
-@Component({
- selector: 'app-import.com',
- imports: [FileUploadModule, ButtonModule,ToastModule,FileUpload],
- templateUrl: './import.com.html',
- styleUrl: './import.com.css'
-})
-export class ImportCom implements OnInit {
- uploadedFiles: any[] = [];
- baseUrl = "";
- private appSetting = inject(AppSettingService);
- constructor(private messageService: MessageService) {}
-ngOnInit(): void
- {
- const cbaseUrl = this.appSetting.appSetting.baseUrl;
- //http://localhost:5015/api/FileUpload/UploadFile"
- this.baseUrl = cbaseUrl + "/api/FileUpload/UploadFile";
- }
-onUpload(event:FileUploadEvent) {
- for(let file of event.files) {
- this.uploadedFiles.push(file);
- }
-
- this.messageService.add({severity: 'info', summary: 'File Uploaded', detail: ''});
- }
-}
+import { Component, inject, OnInit } from '@angular/core';
+import { ButtonModule } from 'primeng/button';
+import { FileUploadEvent, FileUploadModule } from 'primeng/fileupload';
+import { MessageService } from 'primeng/api';
+import { FileUpload } from 'primeng/fileupload';
+import { ToastModule } from 'primeng/toast';
+import { AppSettingService } from '../shares';
+/*
+interface UploadEvent {
+ originalEvent: Event;
+ files: File[];
+}*/
+
+@Component({
+ selector: 'app-import.com',
+ imports: [FileUploadModule, ButtonModule,ToastModule,FileUpload],
+ templateUrl: './import.com.html',
+ styleUrl: './import.com.css'
+})
+export class ImportCom implements OnInit {
+ uploadedFiles: any[] = [];
+ baseUrl = "";
+ private appSetting = inject(AppSettingService);
+ constructor(private messageService: MessageService) {}
+ngOnInit(): void
+ {
+ const cbaseUrl = this.appSetting.appSetting.baseUrl;
+ //http://localhost:5015/api/FileUpload/UploadFile"
+ this.baseUrl = cbaseUrl + "/api/FileUpload/UploadFile";
+ }
+onUpload(event:FileUploadEvent) {
+ for(let file of event.files) {
+ this.uploadedFiles.push(file);
+ }
+
+ this.messageService.add({severity: 'info', summary: 'File Uploaded', detail: ''});
+ }
+}
diff --git a/UI/src/app/import.com/load_and_preview.txt b/UI/src/app/import.com/load_and_preview.txt
index 1d24b19..6e7bde4 100644
--- a/UI/src/app/import.com/load_and_preview.txt
+++ b/UI/src/app/import.com/load_and_preview.txt
@@ -1,12 +1,12 @@
-
-
-
-imgInp.onchange = evt => {
- const [file] = imgInp.files
- if (file) {
- blah.src = URL.createObjectURL(file)
- }
-}
+
+
+
+imgInp.onchange = evt => {
+ const [file] = imgInp.files
+ if (file) {
+ blah.src = URL.createObjectURL(file)
+ }
+}
diff --git a/UI/src/app/login/index.ts b/UI/src/app/login/index.ts
index 262fea5..2ffce8c 100644
--- a/UI/src/app/login/index.ts
+++ b/UI/src/app/login/index.ts
@@ -1,2 +1,2 @@
-export * from './login';
-
+export * from './login';
+
diff --git a/UI/src/app/login/login.css b/UI/src/app/login/login.css
index 52495cb..2339146 100644
--- a/UI/src/app/login/login.css
+++ b/UI/src/app/login/login.css
@@ -1,10 +1,10 @@
-.btnloginRigh {
- display: flex;
- flex-direction:reverse;
- justify-content:flex-end;
- width: 100%;
-}
-.myerror {
- background-color: red;
- color: white;
+.btnloginRigh {
+ display: flex;
+ flex-direction:reverse;
+ justify-content:flex-end;
+ width: 100%;
+}
+.myerror {
+ background-color: red;
+ color: white;
}
\ No newline at end of file
diff --git a/UI/src/app/login/login.html b/UI/src/app/login/login.html
index 46bda9d..47ccc18 100644
--- a/UI/src/app/login/login.html
+++ b/UI/src/app/login/login.html
@@ -1,45 +1,45 @@
-
-
-
+
\ No newline at end of file
diff --git a/UI/src/app/login/login.ts b/UI/src/app/login/login.ts
index 7fcfeac..3de6b2a 100644
--- a/UI/src/app/login/login.ts
+++ b/UI/src/app/login/login.ts
@@ -1,137 +1,137 @@
-import { Component, OnDestroy, OnInit, inject } from '@angular/core';
-import { Router, ActivatedRoute, RouterModule } from '@angular/router';
-import { FormBuilder, Validators, FormGroup } from '@angular/forms';
-import { first } from 'rxjs/operators';
-
-import { AuthenticationService } from '../user-services';
-
-import { ConfirmationService } from 'primeng/api';
-import { Subject, Subscription } from 'rxjs';
-import { CommonModule } from '@angular/common';
-import { CardModule } from 'primeng/card';
-import { ReactiveFormsModule, FormsModule } from '@angular/forms';
-import { ButtonModule } from 'primeng/button';
-import { InputTextModule } from 'primeng/inputtext';
-
-import { AppSettingService, Utils } from '../shares';
-@Component({
- templateUrl: './login.html',
- styleUrl: './login.css',
- selector: 'login',
- imports: [FormsModule, ReactiveFormsModule, ButtonModule, InputTextModule,
- RouterModule, CommonModule, CardModule],
-
-})
-export class Login implements OnInit, OnDestroy {
- loginForm: FormGroup;
- confirmationService = inject(ConfirmationService);
- route = inject(ActivatedRoute);
- router = inject(Router);
- authenticationService = inject(AuthenticationService);
- formBuilder = inject(FormBuilder);
- private appSetting =inject(AppSettingService);
- loading = false;
- homeUrl = "/person";
- submitted = false;
- isChange = true; // disable use false//true for not disable. make sure it true is disable button.
- subChanged$ = new Subject
();
- // get return url from route parameters or default to '/'
- returnUrl = this.route.snapshot.queryParams['returnUrl'] || this.homeUrl;
- error = '';
- private subscription: Subscription = new Subscription();
-
- constructor() {
- const default_us = this.appSetting.appSetting.username;
- const pass = this.appSetting.appSetting.prefill_ps;
- this.loginForm = this.formBuilder.group({
- username: [default_us, Validators.required],
- password: [pass, Validators.required],
-
- });
- }
-
- ngOnInit() {
- // reset login status
- this.loading = false;
- this.subscription.add(this.loginForm.valueChanges.subscribe((x: any) => this.isChange = false));
- this.subscription.add(this.subChanged$.subscribe(x => this.isChange = x));
-
- }
-
- updateData($event: Event) {
- console.log("onChange click", $event);
- }
-
- // convenience getter for easy access to form fields
- get f() { return this.loginForm.value; }
- get isFieldsChange() {
- const chan = this.isChange || !this.loginForm.valid; // this disable so need true valid = true not
- //console.log(this.msg + 'is fields change', chan);
- return chan;
- }
- onSubmit() {
-
- //this.playAudio();
-
- // stop here if form is invalid
- if (this.loginForm.invalid) {
- return;
- }
- const fvalue = this.loginForm.value;
- this.loading = true;
- this.error = "";
- let username = "";
- let password = "";
- if (fvalue.username != null)
- username = fvalue.username;
- if (fvalue.password != null)
- password = fvalue.password;
- this.authenticationService.login(username, password)
- .pipe(first())
- .subscribe({
- next: (x: any) => {
- this.loading = false;
- console.log("login result ", x.data, this.returnUrl);
- if (x.statusCode == 1) {
- // if (x.data.role == 1) {
- // this.homeService.reportDate = Utils.getLastMonth();
- // this.router.navigate(['/addkpi/' + x.data.id + '/' + x.data.measureId], { queryParams: { returnUrl: '/login' } });
-
- // }
- // else if (x.data.role == 3) {
- // this.homeService.reportDate = Utils.getLastMonth();
- // this.router.navigate(['/approval/' + x.data.id], { queryParams: { returnUrl: '/login' } });
-
- //}
- // else
- this.router.navigate([this.homeUrl]);
- }
- else {
- this.loginForm.patchValue({ password: '' });
- // alert("Invalid Username or Password. Please try again.");
- console.error("error in login", x);
- this.confirmationService.confirm({
- message: 'Error in Login: Invalid username or password. May be your password is expired',
- header: 'Error Login',
- icon: 'pi pi-info-circle',
- rejectVisible: false,
- acceptLabel: 'OK',
-
- });
- }
- },
- error: er => {
- console.log("error in login", er);
- this.error = er.message;
- this.loading = false;
- }
- });
-
- }
-
- ngOnDestroy(): void {
- this.subscription.unsubscribe();
- this.loading = false;
- }
-
-}
+import { Component, OnDestroy, OnInit, inject } from '@angular/core';
+import { Router, ActivatedRoute, RouterModule } from '@angular/router';
+import { FormBuilder, Validators, FormGroup } from '@angular/forms';
+import { first } from 'rxjs/operators';
+
+import { AuthenticationService } from '../user-services';
+
+import { ConfirmationService } from 'primeng/api';
+import { Subject, Subscription } from 'rxjs';
+import { CommonModule } from '@angular/common';
+import { CardModule } from 'primeng/card';
+import { ReactiveFormsModule, FormsModule } from '@angular/forms';
+import { ButtonModule } from 'primeng/button';
+import { InputTextModule } from 'primeng/inputtext';
+
+import { AppSettingService, Utils } from '../shares';
+@Component({
+ templateUrl: './login.html',
+ styleUrl: './login.css',
+ selector: 'login',
+ imports: [FormsModule, ReactiveFormsModule, ButtonModule, InputTextModule,
+ RouterModule, CommonModule, CardModule],
+
+})
+export class Login implements OnInit, OnDestroy {
+ loginForm: FormGroup;
+ confirmationService = inject(ConfirmationService);
+ route = inject(ActivatedRoute);
+ router = inject(Router);
+ authenticationService = inject(AuthenticationService);
+ formBuilder = inject(FormBuilder);
+ private appSetting =inject(AppSettingService);
+ loading = false;
+ homeUrl = "/person";
+ submitted = false;
+ isChange = true; // disable use false//true for not disable. make sure it true is disable button.
+ subChanged$ = new Subject();
+ // get return url from route parameters or default to '/'
+ returnUrl = this.route.snapshot.queryParams['returnUrl'] || this.homeUrl;
+ error = '';
+ private subscription: Subscription = new Subscription();
+
+ constructor() {
+ const default_us = this.appSetting.appSetting.username;
+ const pass = this.appSetting.appSetting.prefill_ps;
+ this.loginForm = this.formBuilder.group({
+ username: [default_us, Validators.required],
+ password: [pass, Validators.required],
+
+ });
+ }
+
+ ngOnInit() {
+ // reset login status
+ this.loading = false;
+ this.subscription.add(this.loginForm.valueChanges.subscribe((x: any) => this.isChange = false));
+ this.subscription.add(this.subChanged$.subscribe(x => this.isChange = x));
+
+ }
+
+ updateData($event: Event) {
+ console.log("onChange click", $event);
+ }
+
+ // convenience getter for easy access to form fields
+ get f() { return this.loginForm.value; }
+ get isFieldsChange() {
+ const chan = this.isChange || !this.loginForm.valid; // this disable so need true valid = true not
+ //console.log(this.msg + 'is fields change', chan);
+ return chan;
+ }
+ onSubmit() {
+
+ //this.playAudio();
+
+ // stop here if form is invalid
+ if (this.loginForm.invalid) {
+ return;
+ }
+ const fvalue = this.loginForm.value;
+ this.loading = true;
+ this.error = "";
+ let username = "";
+ let password = "";
+ if (fvalue.username != null)
+ username = fvalue.username;
+ if (fvalue.password != null)
+ password = fvalue.password;
+ this.authenticationService.login(username, password)
+ .pipe(first())
+ .subscribe({
+ next: (x: any) => {
+ this.loading = false;
+ console.log("login result ", x.data, this.returnUrl);
+ if (x.statusCode == 1) {
+ // if (x.data.role == 1) {
+ // this.homeService.reportDate = Utils.getLastMonth();
+ // this.router.navigate(['/addkpi/' + x.data.id + '/' + x.data.measureId], { queryParams: { returnUrl: '/login' } });
+
+ // }
+ // else if (x.data.role == 3) {
+ // this.homeService.reportDate = Utils.getLastMonth();
+ // this.router.navigate(['/approval/' + x.data.id], { queryParams: { returnUrl: '/login' } });
+
+ //}
+ // else
+ this.router.navigate([this.homeUrl]);
+ }
+ else {
+ this.loginForm.patchValue({ password: '' });
+ // alert("Invalid Username or Password. Please try again.");
+ console.error("error in login", x);
+ this.confirmationService.confirm({
+ message: 'Error in Login: Invalid username or password. May be your password is expired',
+ header: 'Error Login',
+ icon: 'pi pi-info-circle',
+ rejectVisible: false,
+ acceptLabel: 'OK',
+
+ });
+ }
+ },
+ error: er => {
+ console.log("error in login", er);
+ this.error = er.message;
+ this.loading = false;
+ }
+ });
+
+ }
+
+ ngOnDestroy(): void {
+ this.subscription.unsubscribe();
+ this.loading = false;
+ }
+
+}
diff --git a/UI/src/app/models/code.ts b/UI/src/app/models/code.ts
index 48e8e84..33e5cf9 100644
--- a/UI/src/app/models/code.ts
+++ b/UI/src/app/models/code.ts
@@ -1,6 +1,6 @@
-export interface Code {
- id:number;
- name:string;
- status?:string;
- active?:boolean;
-}
+export interface Code {
+ id:number;
+ name:string;
+ status?:string;
+ active?:boolean;
+}
diff --git a/UI/src/app/models/configureUrl.ts b/UI/src/app/models/configureUrl.ts
index bf08d34..d59a164 100644
--- a/UI/src/app/models/configureUrl.ts
+++ b/UI/src/app/models/configureUrl.ts
@@ -1,17 +1,17 @@
-export enum ConfigureUrl
-{
- //baseUrl = "http://localhost:61744",
- loginApiUrl = "api/Users/Login",
- searchStaffUrl = "api/Users/SearchADStaff",
- staffUrl = "api/Staff",
- personUrl = "api/Person",
- staffWorkUrl = "api/StaffWork",
- jobUrl ="api/Job",
- clientUrl ="api/Client",
- relationShipUrl ="api/RelationShip",
- logoutUrl ="api/Users/Logout",
- adminUserUrl = "api/Staff",
- lookupUrl = "api/Lookup",
- FileUploadUrl = "api/FileUpload",
-
+export enum ConfigureUrl
+{
+ //baseUrl = "http://localhost:61744",
+ loginApiUrl = "api/Users/Login",
+ searchStaffUrl = "api/Users/SearchADStaff",
+ staffUrl = "api/Staff",
+ personUrl = "api/Person",
+ staffWorkUrl = "api/StaffWork",
+ jobUrl ="api/Job",
+ clientUrl ="api/Client",
+ relationShipUrl ="api/RelationShip",
+ logoutUrl ="api/Users/Logout",
+ adminUserUrl = "api/Staff",
+ lookupUrl = "api/Lookup",
+ FileUploadUrl = "api/FileUpload",
+
}
\ No newline at end of file
diff --git a/UI/src/app/models/enum.ts b/UI/src/app/models/enum.ts
index e41d06c..f8eaf9d 100644
--- a/UI/src/app/models/enum.ts
+++ b/UI/src/app/models/enum.ts
@@ -1,37 +1,37 @@
-export const MIMEType = {
- png: 'image/png',
- jpg: 'image/jpg',
- jpeg: 'image/jpeg',
- gif: 'image/gif',
- txt: 'text/plain',
- pdf: 'application/pdf'
-};
-
-
-//for status of object 0 no change, 1 changed , -1 is deleted.
-export enum mState {
- Delete = -1,
- NoChange = 0,
- Modified = 1,
- New =2,
-};
-
-export enum userRole {
- Normal = 1,
- Admin = 2,
- ServiceManager =3,
- Accounting = 4,
- WorkShop = 5,
-
-};
-
-export enum enumReqStatus {
- Canel = -1,
- Decline = -2,
- NewRequest = 0,
- Allocate = 1,
- Arrival = 2,
- Completed = 3,
- OnHold = 4,
- OffHold = 5
-};
+export const MIMEType = {
+ png: 'image/png',
+ jpg: 'image/jpg',
+ jpeg: 'image/jpeg',
+ gif: 'image/gif',
+ txt: 'text/plain',
+ pdf: 'application/pdf'
+};
+
+
+//for status of object 0 no change, 1 changed , -1 is deleted.
+export enum mState {
+ Delete = -1,
+ NoChange = 0,
+ Modified = 1,
+ New =2,
+};
+
+export enum userRole {
+ Normal = 1,
+ Admin = 2,
+ ServiceManager =3,
+ Accounting = 4,
+ WorkShop = 5,
+
+};
+
+export enum enumReqStatus {
+ Canel = -1,
+ Decline = -2,
+ NewRequest = 0,
+ Allocate = 1,
+ Arrival = 2,
+ Completed = 3,
+ OnHold = 4,
+ OffHold = 5
+};
diff --git a/UI/src/app/models/index.ts b/UI/src/app/models/index.ts
index 66d90ec..a5b7275 100644
--- a/UI/src/app/models/index.ts
+++ b/UI/src/app/models/index.ts
@@ -1,11 +1,11 @@
-export * from './code';
-export * from './configureUrl';
-export * from './user';
-export * from './resultmodel';
-export * from './enum';
-export * from './lookup';
-export * from './staff';
-export * from './person';
-export * from './job';
-export * from './relationship';
+export * from './code';
+export * from './configureUrl';
+export * from './user';
+export * from './resultmodel';
+export * from './enum';
+export * from './lookup';
+export * from './staff';
+export * from './person';
+export * from './job';
+export * from './relationship';
export * from './personphotodto';
\ No newline at end of file
diff --git a/UI/src/app/models/job.ts b/UI/src/app/models/job.ts
index 8c7ab87..5c6bc60 100644
--- a/UI/src/app/models/job.ts
+++ b/UI/src/app/models/job.ts
@@ -1,12 +1,12 @@
-
-export interface JobSearch {
- code:string;
- description:string;
-}
-export interface Job {
- id:number;
- code:string|null|undefined;
- description:string |null|undefined;
- active:boolean |null|undefined;
-
-}
+
+export interface JobSearch {
+ code:string;
+ description:string;
+}
+export interface Job {
+ id:number;
+ code:string|null|undefined;
+ description:string |null|undefined;
+ active:boolean |null|undefined;
+
+}
diff --git a/UI/src/app/models/lookup.ts b/UI/src/app/models/lookup.ts
index 88a1d28..207c826 100644
--- a/UI/src/app/models/lookup.ts
+++ b/UI/src/app/models/lookup.ts
@@ -1,16 +1,16 @@
-// using for priority and infectionType
-export interface Lookup {
- id:number;
- codeId:string;
- description:string;
-
-}
-
-export interface LookupEdit {
- id:number;
- codeId:string;
- description:string;
- active:boolean;
- type:string;
-
+// using for priority and infectionType
+export interface Lookup {
+ id:number;
+ codeId:string;
+ description:string;
+
+}
+
+export interface LookupEdit {
+ id:number;
+ codeId:string;
+ description:string;
+ active:boolean;
+ type:string;
+
}
\ No newline at end of file
diff --git a/UI/src/app/models/mydetail.ts b/UI/src/app/models/mydetail.ts
index 2b5dcbc..d48b13d 100644
--- a/UI/src/app/models/mydetail.ts
+++ b/UI/src/app/models/mydetail.ts
@@ -1,7 +1,7 @@
-export interface MyDetail{
- id:number,
- login:string,
- firstname:string,
- surname:string,
- jobTitle:string,
+export interface MyDetail{
+ id:number,
+ login:string,
+ firstname:string,
+ surname:string,
+ jobTitle:string,
}
\ No newline at end of file
diff --git a/UI/src/app/models/person.ts b/UI/src/app/models/person.ts
index 081535f..062132b 100644
--- a/UI/src/app/models/person.ts
+++ b/UI/src/app/models/person.ts
@@ -1,37 +1,37 @@
-import { PersonPhotoDto } from "./personphotodto";
-import { RelationShip } from "./relationship";
-
-export interface FamilySearch {
- email:string|null;
- phone:string |null;
- clientname:string |null;
-}
-
-export interface Person {
-
- id: number;
- title?:string| null| undefined;
- firstName: string | null|undefined;
- lastName: string | null|undefined;
- email: string | null|undefined;
- phone: string | null|undefined;
- address?: string | null|undefined;
- alive: boolean | null|undefined;
- dob? : string | null|undefined;
- fatherId?: number| null|undefined;
- motherId?: number| null|undefined;
- image?: string|null|undefined;
- sex?: string|null|undefined;
- fatherName?:string |null;
- motherName?:string |null;
- relationShips?: RelationShip[];
- personPhotos?: PersonPhotoDto[];
-}
-
-export interface PersonContainer
-{
- person: Person;
- formData?:FormData;
-
-}
-
+import { PersonPhotoDto } from "./personphotodto";
+import { RelationShip } from "./relationship";
+
+export interface FamilySearch {
+ email:string|null;
+ phone:string |null;
+ clientname:string |null;
+}
+
+export interface Person {
+
+ id: number;
+ title?:string| null| undefined;
+ firstName: string | null|undefined;
+ lastName: string | null|undefined;
+ email: string | null|undefined;
+ phone: string | null|undefined;
+ address?: string | null|undefined;
+ alive: boolean | null|undefined;
+ dob? : string | null|undefined;
+ fatherId?: number| null|undefined;
+ motherId?: number| null|undefined;
+ image?: string|null|undefined;
+ sex?: string|null|undefined;
+ fatherName?:string |null;
+ motherName?:string |null;
+ relationShips?: RelationShip[];
+ personPhotos?: PersonPhotoDto[];
+}
+
+export interface PersonContainer
+{
+ person: Person;
+ formData?:FormData;
+
+}
+
diff --git a/UI/src/app/models/personphotodto.ts b/UI/src/app/models/personphotodto.ts
index 9dbe284..93e56f2 100644
--- a/UI/src/app/models/personphotodto.ts
+++ b/UI/src/app/models/personphotodto.ts
@@ -1,6 +1,6 @@
-export interface PersonPhotoDto {
- id: number;
- photo:string|null | undefined;
- photoType:string |null | undefined;
- file: File | null | undefined;
+export interface PersonPhotoDto {
+ id: number;
+ photo:string|null | undefined;
+ photoType:string |null | undefined;
+ file: File | null | undefined;
}
\ No newline at end of file
diff --git a/UI/src/app/models/relationship.ts b/UI/src/app/models/relationship.ts
index f3a9c7b..2f43d1d 100644
--- a/UI/src/app/models/relationship.ts
+++ b/UI/src/app/models/relationship.ts
@@ -1,27 +1,27 @@
-import { mState } from "./enum";
-
-export interface RelationShip {
- id: number;
- relatePersonId: number;
- personId: number;
- state: mState;
-}
-//relationship id this relatePersonId person has relation with other person
-// also other person has also has relation with you. look in two way
-// person id = 1 has to relation id = 90
-// that means when you edit person 90 their relation ship also be there.
-// so personid = 1 and relation id = 90 we need to show other way around.
-//example first name = Smith has partner Jennifer
-// when we edit Jennifer it should show Smith as her partner too.
-
-export interface RelationShipView {
- id: number;
- relatePersonId:number;
- personId : number;
- pfirstName: string |null |undefined;
- plastName: string |null |undefined;
- sex:string |null |undefined;
- state: mState;
-
-
+import { mState } from "./enum";
+
+export interface RelationShip {
+ id: number;
+ relatePersonId: number;
+ personId: number;
+ state: mState;
+}
+//relationship id this relatePersonId person has relation with other person
+// also other person has also has relation with you. look in two way
+// person id = 1 has to relation id = 90
+// that means when you edit person 90 their relation ship also be there.
+// so personid = 1 and relation id = 90 we need to show other way around.
+//example first name = Smith has partner Jennifer
+// when we edit Jennifer it should show Smith as her partner too.
+
+export interface RelationShipView {
+ id: number;
+ relatePersonId:number;
+ personId : number;
+ pfirstName: string |null |undefined;
+ plastName: string |null |undefined;
+ sex:string |null |undefined;
+ state: mState;
+
+
}
\ No newline at end of file
diff --git a/UI/src/app/models/resultmodel.ts b/UI/src/app/models/resultmodel.ts
index 1ea43e3..33435d1 100644
--- a/UI/src/app/models/resultmodel.ts
+++ b/UI/src/app/models/resultmodel.ts
@@ -1,5 +1,5 @@
-export interface ResultModel {
- data: T;
- message: string;
- statusCode:number;
+export interface ResultModel {
+ data: T;
+ message: string;
+ statusCode:number;
}
\ No newline at end of file
diff --git a/UI/src/app/models/staff.ts b/UI/src/app/models/staff.ts
index bb8d8cd..2b93585 100644
--- a/UI/src/app/models/staff.ts
+++ b/UI/src/app/models/staff.ts
@@ -1,39 +1,39 @@
-export interface StaffView {
- id:number,
- email:string,
- firstname:string,
- lastname:string,
- active:boolean,
-}
-
-export interface ResetPassword{
- id:number;
- password:string;
-}
-
-export interface Staff {
- id:number,
- email:string,
- firstname:string,
- lastname:string,
- phone:string,
- type:number;
- active:boolean;
- roleType: number;
- password?:string,
-}
-
-export interface StaffSearch {
- email:string;
- firstName:string;
- lastName:string;
-}
-
-//this use for new user only.
-export interface AdminUserNew {
- loginId:number;
- user: Staff;// first get userid from this table and
-
-
-}
-
+export interface StaffView {
+ id:number,
+ email:string,
+ firstname:string,
+ lastname:string,
+ active:boolean,
+}
+
+export interface ResetPassword{
+ id:number;
+ password:string;
+}
+
+export interface Staff {
+ id:number,
+ email:string,
+ firstname:string,
+ lastname:string,
+ phone:string,
+ type:number;
+ active:boolean;
+ roleType: number;
+ password?:string,
+}
+
+export interface StaffSearch {
+ email:string;
+ firstName:string;
+ lastName:string;
+}
+
+//this use for new user only.
+export interface AdminUserNew {
+ loginId:number;
+ user: Staff;// first get userid from this table and
+
+
+}
+
diff --git a/UI/src/app/models/user.ts b/UI/src/app/models/user.ts
index 3242e25..c79c3f3 100644
--- a/UI/src/app/models/user.ts
+++ b/UI/src/app/models/user.ts
@@ -1,36 +1,36 @@
-export class User {
- id: number = 0;
- username: string = '';
- role: number = -1;
- firstName: string = '';
- lastName: string = '';
- email:string = '';
- token:string ='';
- position: string ='';
- department: string = '';
- managerEmail:string ='';
- phone:string='';
-
-}
-
-export interface UserAD
-{
- id: number,
- firstName: string,
- lastName: string,
- email: string,
- username: string,
- department: string,
- position: string,
- role: number,
- phone:string,
- managerEmail:string
- }
-
-export class SecAccessLevel {
- accessName:string='';
- request:number=0;
- report:number=0;
- admin:number=0;
- allocation:number=0;
-}
+export class User {
+ id: number = 0;
+ username: string = '';
+ role: number = -1;
+ firstName: string = '';
+ lastName: string = '';
+ email:string = '';
+ token:string ='';
+ position: string ='';
+ department: string = '';
+ managerEmail:string ='';
+ phone:string='';
+
+}
+
+export interface UserAD
+{
+ id: number,
+ firstName: string,
+ lastName: string,
+ email: string,
+ username: string,
+ department: string,
+ position: string,
+ role: number,
+ phone:string,
+ managerEmail:string
+ }
+
+export class SecAccessLevel {
+ accessName:string='';
+ request:number=0;
+ report:number=0;
+ admin:number=0;
+ allocation:number=0;
+}
diff --git a/UI/src/app/mythem.ts b/UI/src/app/mythem.ts
index 884d922..9747d77 100644
--- a/UI/src/app/mythem.ts
+++ b/UI/src/app/mythem.ts
@@ -1,71 +1,71 @@
-
-
-//mypreset.ts
-import { definePreset } from '@primeuix/themes';
-import Aura from '@primeuix/themes/aura';
-import { primitive } from '@primeuix/themes/aura/base';
-
-const MyPreset = definePreset(Aura, {
- semantic: {
- colorScheme: {
- primitive: {
- cyan: {
- 50: '{cyan.50}',
- 100: '{cyan.100}',
- 200: '{cyan.200}',
- 300: '{cyan.300}',
- 400: '{cyan.400}',
- 500: '{cyan.500}',
- }
-
- },
- primary: {
- 50: '{zinc.50}',
- 100: '{zinc.100}',
- 200: '{zinc.200}',
- 300: '{zinc.300}',
- 400: '{zinc.400}',
- 500: '{zinc.500}',
- 600: '{zinc.600}',
- 700: '{zinc.700}',
- 800: '{zinc.800}',
- 900: '{zinc.900}',
- 950: '{zinc.950}'
- },
- light: {
- surface: {
- 0: '#ffffff',
- 50: '{zinc.50}',
- 100: '{zinc.100}',
- 200: '{zinc.200}',
- 300: '{zinc.300}',
- 400: '{zinc.400}',
- 500: '{zinc.500}',
- 600: '{zinc.600}',
- 700: '{zinc.700}',
- 800: '{zinc.800}',
- 900: '{zinc.900}',
- 950: '{zinc.950}'
- }
- },
- dark: {
- surface: {
- 0: '#ffffff',
- 50: '{slate.50}',
- 100: '{slate.100}',
- 200: '{slate.200}',
- 300: '{slate.300}',
- 400: '{slate.400}',
- 500: '{slate.500}',
- 600: '{slate.600}',
- 700: '{slate.700}',
- 800: '{slate.800}',
- 900: '{slate.900}',
- 950: '{slate.950}'
- }
- }
- }
- }
-});
-
-export default MyPreset;
+
+
+//mypreset.ts
+import { definePreset } from '@primeuix/themes';
+import Aura from '@primeuix/themes/aura';
+import { primitive } from '@primeuix/themes/aura/base';
+
+const MyPreset = definePreset(Aura, {
+ semantic: {
+ colorScheme: {
+ primitive: {
+ cyan: {
+ 50: '{cyan.50}',
+ 100: '{cyan.100}',
+ 200: '{cyan.200}',
+ 300: '{cyan.300}',
+ 400: '{cyan.400}',
+ 500: '{cyan.500}',
+ }
+
+ },
+ primary: {
+ 50: '{zinc.50}',
+ 100: '{zinc.100}',
+ 200: '{zinc.200}',
+ 300: '{zinc.300}',
+ 400: '{zinc.400}',
+ 500: '{zinc.500}',
+ 600: '{zinc.600}',
+ 700: '{zinc.700}',
+ 800: '{zinc.800}',
+ 900: '{zinc.900}',
+ 950: '{zinc.950}'
+ },
+ light: {
+ surface: {
+ 0: '#ffffff',
+ 50: '{zinc.50}',
+ 100: '{zinc.100}',
+ 200: '{zinc.200}',
+ 300: '{zinc.300}',
+ 400: '{zinc.400}',
+ 500: '{zinc.500}',
+ 600: '{zinc.600}',
+ 700: '{zinc.700}',
+ 800: '{zinc.800}',
+ 900: '{zinc.900}',
+ 950: '{zinc.950}'
+ }
+ },
+ dark: {
+ surface: {
+ 0: '#ffffff',
+ 50: '{slate.50}',
+ 100: '{slate.100}',
+ 200: '{slate.200}',
+ 300: '{slate.300}',
+ 400: '{slate.400}',
+ 500: '{slate.500}',
+ 600: '{slate.600}',
+ 700: '{slate.700}',
+ 800: '{slate.800}',
+ 900: '{slate.900}',
+ 950: '{slate.950}'
+ }
+ }
+ }
+ }
+});
+
+export default MyPreset;
diff --git a/UI/src/app/person/family.orga.css b/UI/src/app/person/family.orga.css
index 3db3368..1c17f21 100644
--- a/UI/src/app/person/family.orga.css
+++ b/UI/src/app/person/family.orga.css
@@ -1,12 +1,12 @@
-table {
- width: 100%;
-}
-
-.mat-form-field {
- font-size: 14px;
- width: 100%;
-}
-td.mat-column-edit, .mat-column-delete {
- width: 35px;
- padding-right: 2px;
-}
+table {
+ width: 100%;
+}
+
+.mat-form-field {
+ font-size: 14px;
+ width: 100%;
+}
+td.mat-column-edit, .mat-column-delete {
+ width: 35px;
+ padding-right: 2px;
+}
diff --git a/UI/src/app/person/family.orga.html b/UI/src/app/person/family.orga.html
index 0b93465..991d56d 100644
--- a/UI/src/app/person/family.orga.html
+++ b/UI/src/app/person/family.orga.html
@@ -1,39 +1,39 @@
-
-
Family Tree
-
-
-
-
-
-
-
-
-
-
- @if (node.data.image != "")
- {
-
![]()
- }
-
-
{{ node.label }}
-
{{ node.data.sex }}
-
- @if (node.data.image != "")
- {
-
-
-
-
![person profile]()
-
- }
-
-
-
-
-
-
-
-
-
-
+
+
Family Tree
+
+
+
+
+
+
+
+
+
+
+ @if (node.data.image != "")
+ {
+
![]()
+ }
+
+
{{ node.label }}
+
{{ node.data.sex }}
+
+ @if (node.data.image != "")
+ {
+
+
+
+
![person profile]()
+
+ }
+
+
+
+
+
+
+
+
+
+
diff --git a/UI/src/app/person/family.orga.ts b/UI/src/app/person/family.orga.ts
index 95bf5b4..f4b4a91 100644
--- a/UI/src/app/person/family.orga.ts
+++ b/UI/src/app/person/family.orga.ts
@@ -1,256 +1,256 @@
-import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal} from '@angular/core';
-
-import { StaffView ,StaffSearch, Person, RelationShip } from '../models';
-import { OrganizationChartModule } from 'primeng/organizationchart';
-
-import { take } from 'rxjs/operators';
-import { Subscription } from 'rxjs';
-import { PersonService } from './person.service';
-import { TableModule } from 'primeng/table';
-import { FormsModule } from '@angular/forms';
-import { CommonModule } from '@angular/common';
-import { ButtonModule } from 'primeng/button';
-import { TreeNode } from 'primeng/api';
-import { InputTextModule } from 'primeng/inputtext';
-import { DialogService, DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
-import { PersonEdit } from './person.edit';
-import { MessageService } from 'primeng/api';
-import { TreeModule, TreeNodeDoubleClickEvent, TreeNodeSelectEvent } from 'primeng/tree';
-import { Utils } from '../shares';
-import { PopoverModule } from 'primeng/popover';
-
-@Component({
- selector: 'family-orga',
- templateUrl: './family.orga.html',
- imports:[TableModule,FormsModule,TreeModule,PopoverModule, OrganizationChartModule,CommonModule,ButtonModule,InputTextModule],
- styleUrls: ['./family.orga.css'],
- providers: [DialogService]
-})
-export class FamilyOrga implements OnInit, OnDestroy{
-
- private subscription:Subscription = new Subscription();
- person!: Person;
- selectedNode!: TreeNode;
- selectedNodes!: TreeNode[];
- private cd = inject(ChangeDetectorRef);
- familyTree = signal
([]);
- relations: RelationShip[] =[];
- _id = -10;
- loading = false;
- familyList:Person[] = [];
- msg ="[Person organise component]";
- private messageService = inject(MessageService);
- public ref = inject(DynamicDialogRef);
- public config = inject(DynamicDialogConfig);
- /*
- private store: Store
- */
- private personService = inject(PersonService);
- public dialogService = inject(DialogService);
-
- ngOnInit(): void
- {
- const id = this.config.data.id;
- this.familyList = this.config.data.familyList;
- const item = this.familyList.find(x => x.id == id);
- if (item != undefined)
- this.person = item;
- this.loadPersonFamilyTree(id);
- //this.populateTree();
-
- }
- populateTree() : void {
- const data: TreeNode[] = [
- {
- expanded: true,
- type: 'person',
- styleClass: 'bg-indigo-500 text-white',
- data: {
- image: 'https://primefaces.org/cdn/primeng/images/demo/avatar/amyelsner.png',
- name: 'Amy Elsner',
- title: 'CEO'
- },
- children: [
- {
- expanded: true,
- type: 'person',
- styleClass: 'bg-purple-500 text-white',
- data: {
- image: 'https://primefaces.org/cdn/primeng/images/demo/avatar/annafali.png',
- name: 'Anna Fali',
- title: 'CMO'
- },
- children: [
- {
- label: 'Sales',
- styleClass: 'bg-purple-500 text-white',
- style: ' border-radius: 12px'
- },
- {
- label: 'Marketing',
- styleClass: 'bg-purple-500 text-white',
- style: ' border-radius: 12px'
- }
- ]
- },
- {
- expanded: true,
- type: 'person',
- styleClass: 'bg-teal-500 text-white',
- data: {
- image: 'https://primefaces.org/cdn/primeng/images/demo/avatar/stephenshaw.png',
- name: 'Stephen Shaw',
- title: 'CTO'
- },
- children: [
- {
- label: 'Development',
- styleClass: 'bg-teal-500 text-white'
- },
- {
- label: 'UI/UX Design',
- styleClass: 'bg-teal-500 text-white'
- }
- ]
- }
- ]
- }
- ];
- this.familyTree.set(data);
- }
- loadPersonFamilyTree(id: number): void {
- const relationShip$ = this.personService.loadPersonFamily(id);
- this.subscription.add(relationShip$.subscribe(
- {
- next: x => {
- if (x.statusCode == 1)
- {
- let tree : TreeNode[] =[];
- tree.push(x.data);
- this.familyTree.set(tree);
- console.log("load person family", this.familyTree());
-
- }
- },
- error: e => {
- console.error("error load loadPersonFamily by personId", id);
- this.messageService.add({severity:'error', summary: 'error load loadPersonFamily by personId', detail: e.message});
- }
- }
- ));
- }
-
- nodeSelect(event: TreeNodeSelectEvent) {
- // this.messageService.add({ severity: 'info', summary: 'Node Selected', detail: event.node.label });
- }
-nodeDoubleSelect(event:TreeNodeDoubleClickEvent) : void {
- // console.log("double click node", event.node.key);
- const id = event.node.key;
- this.edit(Number(id));
-}
-
- getName(id:number): string
- {
- let result ="";
- const item = this.familyList.find(x => x.id == id);
- if (item)
- result = item.lastName + " " + item.firstName;
- return result;
- }
- updateParent(list:Person[]):void {
- let i = 0;
- let item:Person;
- for (i = 0; i< list.length; i++)
- {
- item = list[i];
- if (item.fatherId && item.fatherId > 0)
- {
- item.fatherName = this.getName(item.fatherId);
- }
- }
- }
-
- newFamily():void {
- //console.log("add new employee");
- this.personService.parentList = this.familyList;
- // this.router.navigate( ['/family/new'], { queryParams: {returnUrl:'/family' } });
- this.showEdit(this._id--);
- }
- edit(id: number) : void {
- //console.log("edit family", id);
- this.personService.parentList = this.familyList;
- // this.router.navigate( ['/family/'+id], { queryParams: {returnUrl:'/family' } });
- this.showEdit(id);
- }
- showEdit(id:number) {
- const ref = this.dialogService.open(PersonEdit, {
- data: {
- id,
- familyList: this.familyList,
- },
- header: 'Family',
- width: '80%',
- maximizable: true
- });
- if (ref)
- {
- ref.onClose.subscribe((item: Person) => {
- if (item) {
- //console.log("after close ward edit", item);
- this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!});
- //update the current list
- this.updateList(item);
- }
- });
- }
- }
- updateList(item: Person) :void {
- const idx = this.familyList.findIndex( x => x.id == item.id);
- if (item.fatherId && item.fatherId > 0)
- item.fatherName = this.getName(item.fatherId);
- if (item.motherId && item.motherId > 0)
- item.motherName = this.getName(item.motherId);
- if (idx < 0)
- {
-
- const olist = [... this.familyList, item];
- this.familyList = olist;
- }
- else
- {
- const oitem = this.familyList[idx];
- oitem.firstName = item.firstName;
- oitem.lastName = item.lastName;
- oitem.address = item.address;
- oitem.alive = item.alive;
- oitem.dob = item.dob;
- oitem.email = item.email;
- oitem.fatherId = item.fatherId;
- oitem.motherId = item.motherId;
- oitem.motherName = item.motherName;
- oitem.fatherName = item.fatherName;
-
- }
- }
- deleteItem(id: number): void {
- this.personService.deletePerson(id)
- .pipe(take(1))
- .subscribe({ next: result => {
- console.log(this.msg + " deleteItem success", result);
-
- const nlist = this.familyList.filter(d => d.id !== id);
- this.familyList = nlist;
- },
- error: e => console.error(e)
- });
- //console.log(this.msg + "click button to delete");
- }
- close() :void {
- this.ref.close(null);
- }
-
- ngOnDestroy() {
- this.subscription.unsubscribe();
- }
-}
-
+import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal} from '@angular/core';
+
+import { StaffView ,StaffSearch, Person, RelationShip } from '../models';
+import { OrganizationChartModule } from 'primeng/organizationchart';
+
+import { take } from 'rxjs/operators';
+import { Subscription } from 'rxjs';
+import { PersonService } from './person.service';
+import { TableModule } from 'primeng/table';
+import { FormsModule } from '@angular/forms';
+import { CommonModule } from '@angular/common';
+import { ButtonModule } from 'primeng/button';
+import { TreeNode } from 'primeng/api';
+import { InputTextModule } from 'primeng/inputtext';
+import { DialogService, DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
+import { PersonEdit } from './person.edit';
+import { MessageService } from 'primeng/api';
+import { TreeModule, TreeNodeDoubleClickEvent, TreeNodeSelectEvent } from 'primeng/tree';
+import { Utils } from '../shares';
+import { PopoverModule } from 'primeng/popover';
+
+@Component({
+ selector: 'family-orga',
+ templateUrl: './family.orga.html',
+ imports:[TableModule,FormsModule,TreeModule,PopoverModule, OrganizationChartModule,CommonModule,ButtonModule,InputTextModule],
+ styleUrls: ['./family.orga.css'],
+ providers: [DialogService]
+})
+export class FamilyOrga implements OnInit, OnDestroy{
+
+ private subscription:Subscription = new Subscription();
+ person!: Person;
+ selectedNode!: TreeNode;
+ selectedNodes!: TreeNode[];
+ private cd = inject(ChangeDetectorRef);
+ familyTree = signal([]);
+ relations: RelationShip[] =[];
+ _id = -10;
+ loading = false;
+ familyList:Person[] = [];
+ msg ="[Person organise component]";
+ private messageService = inject(MessageService);
+ public ref = inject(DynamicDialogRef);
+ public config = inject(DynamicDialogConfig);
+ /*
+ private store: Store
+ */
+ private personService = inject(PersonService);
+ public dialogService = inject(DialogService);
+
+ ngOnInit(): void
+ {
+ const id = this.config.data.id;
+ this.familyList = this.config.data.familyList;
+ const item = this.familyList.find(x => x.id == id);
+ if (item != undefined)
+ this.person = item;
+ this.loadPersonFamilyTree(id);
+ //this.populateTree();
+
+ }
+ populateTree() : void {
+ const data: TreeNode[] = [
+ {
+ expanded: true,
+ type: 'person',
+ styleClass: 'bg-indigo-500 text-white',
+ data: {
+ image: 'https://primefaces.org/cdn/primeng/images/demo/avatar/amyelsner.png',
+ name: 'Amy Elsner',
+ title: 'CEO'
+ },
+ children: [
+ {
+ expanded: true,
+ type: 'person',
+ styleClass: 'bg-purple-500 text-white',
+ data: {
+ image: 'https://primefaces.org/cdn/primeng/images/demo/avatar/annafali.png',
+ name: 'Anna Fali',
+ title: 'CMO'
+ },
+ children: [
+ {
+ label: 'Sales',
+ styleClass: 'bg-purple-500 text-white',
+ style: ' border-radius: 12px'
+ },
+ {
+ label: 'Marketing',
+ styleClass: 'bg-purple-500 text-white',
+ style: ' border-radius: 12px'
+ }
+ ]
+ },
+ {
+ expanded: true,
+ type: 'person',
+ styleClass: 'bg-teal-500 text-white',
+ data: {
+ image: 'https://primefaces.org/cdn/primeng/images/demo/avatar/stephenshaw.png',
+ name: 'Stephen Shaw',
+ title: 'CTO'
+ },
+ children: [
+ {
+ label: 'Development',
+ styleClass: 'bg-teal-500 text-white'
+ },
+ {
+ label: 'UI/UX Design',
+ styleClass: 'bg-teal-500 text-white'
+ }
+ ]
+ }
+ ]
+ }
+ ];
+ this.familyTree.set(data);
+ }
+ loadPersonFamilyTree(id: number): void {
+ const relationShip$ = this.personService.loadPersonFamily(id);
+ this.subscription.add(relationShip$.subscribe(
+ {
+ next: x => {
+ if (x.statusCode == 1)
+ {
+ let tree : TreeNode[] =[];
+ tree.push(x.data);
+ this.familyTree.set(tree);
+ console.log("load person family", this.familyTree());
+
+ }
+ },
+ error: e => {
+ console.error("error load loadPersonFamily by personId", id);
+ this.messageService.add({severity:'error', summary: 'error load loadPersonFamily by personId', detail: e.message});
+ }
+ }
+ ));
+ }
+
+ nodeSelect(event: TreeNodeSelectEvent) {
+ // this.messageService.add({ severity: 'info', summary: 'Node Selected', detail: event.node.label });
+ }
+nodeDoubleSelect(event:TreeNodeDoubleClickEvent) : void {
+ // console.log("double click node", event.node.key);
+ const id = event.node.key;
+ this.edit(Number(id));
+}
+
+ getName(id:number): string
+ {
+ let result ="";
+ const item = this.familyList.find(x => x.id == id);
+ if (item)
+ result = item.lastName + " " + item.firstName;
+ return result;
+ }
+ updateParent(list:Person[]):void {
+ let i = 0;
+ let item:Person;
+ for (i = 0; i< list.length; i++)
+ {
+ item = list[i];
+ if (item.fatherId && item.fatherId > 0)
+ {
+ item.fatherName = this.getName(item.fatherId);
+ }
+ }
+ }
+
+ newFamily():void {
+ //console.log("add new employee");
+ this.personService.parentList = this.familyList;
+ // this.router.navigate( ['/family/new'], { queryParams: {returnUrl:'/family' } });
+ this.showEdit(this._id--);
+ }
+ edit(id: number) : void {
+ //console.log("edit family", id);
+ this.personService.parentList = this.familyList;
+ // this.router.navigate( ['/family/'+id], { queryParams: {returnUrl:'/family' } });
+ this.showEdit(id);
+ }
+ showEdit(id:number) {
+ const ref = this.dialogService.open(PersonEdit, {
+ data: {
+ id,
+ familyList: this.familyList,
+ },
+ header: 'Family',
+ width: '80%',
+ maximizable: true
+ });
+ if (ref)
+ {
+ ref.onClose.subscribe((item: Person) => {
+ if (item) {
+ //console.log("after close ward edit", item);
+ this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!});
+ //update the current list
+ this.updateList(item);
+ }
+ });
+ }
+ }
+ updateList(item: Person) :void {
+ const idx = this.familyList.findIndex( x => x.id == item.id);
+ if (item.fatherId && item.fatherId > 0)
+ item.fatherName = this.getName(item.fatherId);
+ if (item.motherId && item.motherId > 0)
+ item.motherName = this.getName(item.motherId);
+ if (idx < 0)
+ {
+
+ const olist = [... this.familyList, item];
+ this.familyList = olist;
+ }
+ else
+ {
+ const oitem = this.familyList[idx];
+ oitem.firstName = item.firstName;
+ oitem.lastName = item.lastName;
+ oitem.address = item.address;
+ oitem.alive = item.alive;
+ oitem.dob = item.dob;
+ oitem.email = item.email;
+ oitem.fatherId = item.fatherId;
+ oitem.motherId = item.motherId;
+ oitem.motherName = item.motherName;
+ oitem.fatherName = item.fatherName;
+
+ }
+ }
+ deleteItem(id: number): void {
+ this.personService.deletePerson(id)
+ .pipe(take(1))
+ .subscribe({ next: result => {
+ console.log(this.msg + " deleteItem success", result);
+
+ const nlist = this.familyList.filter(d => d.id !== id);
+ this.familyList = nlist;
+ },
+ error: e => console.error(e)
+ });
+ //console.log(this.msg + "click button to delete");
+ }
+ close() :void {
+ this.ref.close(null);
+ }
+
+ ngOnDestroy() {
+ this.subscription.unsubscribe();
+ }
+}
+
diff --git a/UI/src/app/person/family.tree.css b/UI/src/app/person/family.tree.css
index 3db3368..1c17f21 100644
--- a/UI/src/app/person/family.tree.css
+++ b/UI/src/app/person/family.tree.css
@@ -1,12 +1,12 @@
-table {
- width: 100%;
-}
-
-.mat-form-field {
- font-size: 14px;
- width: 100%;
-}
-td.mat-column-edit, .mat-column-delete {
- width: 35px;
- padding-right: 2px;
-}
+table {
+ width: 100%;
+}
+
+.mat-form-field {
+ font-size: 14px;
+ width: 100%;
+}
+td.mat-column-edit, .mat-column-delete {
+ width: 35px;
+ padding-right: 2px;
+}
diff --git a/UI/src/app/person/family.tree.html b/UI/src/app/person/family.tree.html
index 905b0be..1d8db2f 100644
--- a/UI/src/app/person/family.tree.html
+++ b/UI/src/app/person/family.tree.html
@@ -1,35 +1,35 @@
-
-
Family Tree by Person has Father
-
-
-
+
+
Family Tree by Person has Father
+
+
+
diff --git a/UI/src/app/person/family.tree.ts b/UI/src/app/person/family.tree.ts
index afe8a3f..d3f9f6f 100644
--- a/UI/src/app/person/family.tree.ts
+++ b/UI/src/app/person/family.tree.ts
@@ -1,227 +1,227 @@
-import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal} from '@angular/core';
-
-import { StaffView ,StaffSearch, Person } from '../models';
-import { OrganizationChartModule } from 'primeng/organizationchart';
-
-import { take } from 'rxjs/operators';
-import { Subscription } from 'rxjs';
-import { Router } from '@angular/router';
-import { PersonService } from './person.service';
-import { AuthenticationService } from '../user-services';
-import { TableModule } from 'primeng/table';
-import { FormsModule } from '@angular/forms';
-import { CommonModule } from '@angular/common';
-import { ButtonModule } from 'primeng/button';
-import { TreeNode } from 'primeng/api';
-import { InputTextModule } from 'primeng/inputtext';
-import { DialogService } from 'primeng/dynamicdialog';
-import { PersonEdit } from './person.edit';
-import { MessageService } from 'primeng/api';
-import { CheckboxModule } from 'primeng/checkbox';
-import { TreeModule, TreeNodeDoubleClickEvent, TreeNodeSelectEvent } from 'primeng/tree';
-import { Utils } from '../shares';
-
-@Component({
- selector: 'family-tree',
- templateUrl: './family.tree.html',
- imports:[TableModule,FormsModule,TreeModule,CheckboxModule,
- OrganizationChartModule,CommonModule,
- FormsModule,
- ButtonModule,InputTextModule],
- styleUrls: ['./family.tree.css'],
- providers: [DialogService]
-})
-export class FamilyTree implements OnInit, OnDestroy{
- isFather = signal(true);
- private subscription:Subscription = new Subscription();
- firstname = '';
- selectedNode!: TreeNode;
- private cd = inject(ChangeDetectorRef);
- familyTree: TreeNode[] = [];
- email = '';
- lastname = '';
- _id = -10;
- loading = false;
- familyList:Person[] = [];
- msg ="[Person tree component]";
- private messageService = inject(MessageService);
- /*
- private store: Store
- */
- constructor(
- public dialogService: DialogService,
- private personService: PersonService,
- private authenticationService: AuthenticationService,
- private router: Router
- ) {}
- getSearchCiteria(): StaffSearch {
- let criteria:StaffSearch = {
- email: this.email,
- firstName: this.firstname,
- lastName: this.lastname,
- };
-
- return criteria;
-
- }
- canSearch():boolean {
- let result = false;
- result = this.email !== "";
- result = result || this.lastname !== "";
- result = result || this.firstname !== "";
- return result;
- }
- ngOnInit(): void
- {
- this.authenticationService.isHome = false;
- this.authenticationService.isReport = false;
- const prev = this.personService.searchCriteria;
- let goload = true;
- this.load();
-
- }
- getActive(active:boolean):string {
- let result = 'false-icon pi-times-circle';
- if (active)
- result = 'true-icon pi-check-circle';
- return result;
- }
- load():void {
- let father = this.isFather();
- this.loading = true;
- //const criteria = this.getSearchCiteria();
- // this.personService.searchCriteria = criteria;
- this.subscription.add(
- this.personService.loadPersonFamilyTree(father, !father).subscribe(
- {
- next: x => {
- if (x.statusCode == 1)
- {
- this.familyTree = x.data;
- this.loading = false;
- this.cd.detectChanges();
- console.log("the family tree", this.familyTree);
- }
- },
-
- error: e => {
- const message = e || e.message;
- // this.toastr.error(message);
- this.loading = false;
- console.log("error ", e);
- }
- })
- );
- }
-
- nodeSelect(event: TreeNodeSelectEvent) {
- // this.messageService.add({ severity: 'info', summary: 'Node Selected', detail: event.node.label });
- }
-nodeDoubleSelect(event:TreeNodeDoubleClickEvent) : void {
- // console.log("double click node", event.node.key);
- const id = event.node.key;
- this.edit(Number(id));
-}
-
- getName(id:number): string
- {
- let result ="";
- const item = this.familyList.find(x => x.id == id);
- if (item)
- result = item.lastName + " " + item.firstName;
- return result;
- }
- updateParent(list:Person[]):void {
- let i = 0;
- let item:Person;
- for (i = 0; i< list.length; i++)
- {
- item = list[i];
- if (item.fatherId && item.fatherId > 0)
- {
- item.fatherName = this.getName(item.fatherId);
- }
- }
- }
-
- newFamily():void {
- //console.log("add new employee");
- this.personService.parentList = this.familyList;
- // this.router.navigate( ['/family/new'], { queryParams: {returnUrl:'/family' } });
- this.showEdit(this._id--);
- }
- edit(id: number) : void {
- //console.log("edit family", id);
- this.personService.parentList = this.familyList;
- // this.router.navigate( ['/family/'+id], { queryParams: {returnUrl:'/family' } });
- this.showEdit(id);
- }
- showEdit(id:number) {
- const ref = this.dialogService.open(PersonEdit, {
- data: {
- id,
- familyList: this.familyList,
- },
- header: 'Family',
- width: '80%',
- maximizable: true
- });
- if (ref)
- {
- ref.onClose.subscribe((item: Person) => {
- if (item) {
- //console.log("after close ward edit", item);
- this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!});
- //update the current list
- this.updateList(item);
- }
- });
- }
- }
- updateList(item: Person) :void {
- const idx = this.familyList.findIndex( x => x.id == item.id);
- if (item.fatherId && item.fatherId > 0)
- item.fatherName = this.getName(item.fatherId);
- if (item.motherId && item.motherId > 0)
- item.motherName = this.getName(item.motherId);
- if (idx < 0)
- {
-
- const olist = [... this.familyList, item];
- this.familyList = olist;
- }
- else
- {
- const oitem = this.familyList[idx];
- oitem.firstName = item.firstName;
- oitem.lastName = item.lastName;
- oitem.address = item.address;
- oitem.alive = item.alive;
- oitem.dob = item.dob;
- oitem.email = item.email;
- oitem.fatherId = item.fatherId;
- oitem.motherId = item.motherId;
- oitem.motherName = item.motherName;
- oitem.fatherName = item.fatherName;
-
- }
- }
- deleteItem(id: number): void {
- this.personService.deletePerson(id)
- .pipe(take(1))
- .subscribe({ next: result => {
- console.log(this.msg + " deleteItem success", result);
-
- const nlist = this.familyList.filter(d => d.id !== id);
- this.familyList = nlist;
- },
- error: e => console.error(e)
- });
- //console.log(this.msg + "click button to delete");
- }
-
- ngOnDestroy() {
- this.subscription.unsubscribe();
- }
-}
-
+import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal} from '@angular/core';
+
+import { StaffView ,StaffSearch, Person } from '../models';
+import { OrganizationChartModule } from 'primeng/organizationchart';
+
+import { take } from 'rxjs/operators';
+import { Subscription } from 'rxjs';
+import { Router } from '@angular/router';
+import { PersonService } from './person.service';
+import { AuthenticationService } from '../user-services';
+import { TableModule } from 'primeng/table';
+import { FormsModule } from '@angular/forms';
+import { CommonModule } from '@angular/common';
+import { ButtonModule } from 'primeng/button';
+import { TreeNode } from 'primeng/api';
+import { InputTextModule } from 'primeng/inputtext';
+import { DialogService } from 'primeng/dynamicdialog';
+import { PersonEdit } from './person.edit';
+import { MessageService } from 'primeng/api';
+import { CheckboxModule } from 'primeng/checkbox';
+import { TreeModule, TreeNodeDoubleClickEvent, TreeNodeSelectEvent } from 'primeng/tree';
+import { Utils } from '../shares';
+
+@Component({
+ selector: 'family-tree',
+ templateUrl: './family.tree.html',
+ imports:[TableModule,FormsModule,TreeModule,CheckboxModule,
+ OrganizationChartModule,CommonModule,
+ FormsModule,
+ ButtonModule,InputTextModule],
+ styleUrls: ['./family.tree.css'],
+ providers: [DialogService]
+})
+export class FamilyTree implements OnInit, OnDestroy{
+ isFather = signal(true);
+ private subscription:Subscription = new Subscription();
+ firstname = '';
+ selectedNode!: TreeNode;
+ private cd = inject(ChangeDetectorRef);
+ familyTree: TreeNode[] = [];
+ email = '';
+ lastname = '';
+ _id = -10;
+ loading = false;
+ familyList:Person[] = [];
+ msg ="[Person tree component]";
+ private messageService = inject(MessageService);
+ /*
+ private store: Store
+ */
+ constructor(
+ public dialogService: DialogService,
+ private personService: PersonService,
+ private authenticationService: AuthenticationService,
+ private router: Router
+ ) {}
+ getSearchCiteria(): StaffSearch {
+ let criteria:StaffSearch = {
+ email: this.email,
+ firstName: this.firstname,
+ lastName: this.lastname,
+ };
+
+ return criteria;
+
+ }
+ canSearch():boolean {
+ let result = false;
+ result = this.email !== "";
+ result = result || this.lastname !== "";
+ result = result || this.firstname !== "";
+ return result;
+ }
+ ngOnInit(): void
+ {
+ this.authenticationService.isHome = false;
+ this.authenticationService.isReport = false;
+ const prev = this.personService.searchCriteria;
+ let goload = true;
+ this.load();
+
+ }
+ getActive(active:boolean):string {
+ let result = 'false-icon pi-times-circle';
+ if (active)
+ result = 'true-icon pi-check-circle';
+ return result;
+ }
+ load():void {
+ let father = this.isFather();
+ this.loading = true;
+ //const criteria = this.getSearchCiteria();
+ // this.personService.searchCriteria = criteria;
+ this.subscription.add(
+ this.personService.loadPersonFamilyTree(father, !father).subscribe(
+ {
+ next: x => {
+ if (x.statusCode == 1)
+ {
+ this.familyTree = x.data;
+ this.loading = false;
+ this.cd.detectChanges();
+ console.log("the family tree", this.familyTree);
+ }
+ },
+
+ error: e => {
+ const message = e || e.message;
+ // this.toastr.error(message);
+ this.loading = false;
+ console.log("error ", e);
+ }
+ })
+ );
+ }
+
+ nodeSelect(event: TreeNodeSelectEvent) {
+ // this.messageService.add({ severity: 'info', summary: 'Node Selected', detail: event.node.label });
+ }
+nodeDoubleSelect(event:TreeNodeDoubleClickEvent) : void {
+ // console.log("double click node", event.node.key);
+ const id = event.node.key;
+ this.edit(Number(id));
+}
+
+ getName(id:number): string
+ {
+ let result ="";
+ const item = this.familyList.find(x => x.id == id);
+ if (item)
+ result = item.lastName + " " + item.firstName;
+ return result;
+ }
+ updateParent(list:Person[]):void {
+ let i = 0;
+ let item:Person;
+ for (i = 0; i< list.length; i++)
+ {
+ item = list[i];
+ if (item.fatherId && item.fatherId > 0)
+ {
+ item.fatherName = this.getName(item.fatherId);
+ }
+ }
+ }
+
+ newFamily():void {
+ //console.log("add new employee");
+ this.personService.parentList = this.familyList;
+ // this.router.navigate( ['/family/new'], { queryParams: {returnUrl:'/family' } });
+ this.showEdit(this._id--);
+ }
+ edit(id: number) : void {
+ //console.log("edit family", id);
+ this.personService.parentList = this.familyList;
+ // this.router.navigate( ['/family/'+id], { queryParams: {returnUrl:'/family' } });
+ this.showEdit(id);
+ }
+ showEdit(id:number) {
+ const ref = this.dialogService.open(PersonEdit, {
+ data: {
+ id,
+ familyList: this.familyList,
+ },
+ header: 'Family',
+ width: '80%',
+ maximizable: true
+ });
+ if (ref)
+ {
+ ref.onClose.subscribe((item: Person) => {
+ if (item) {
+ //console.log("after close ward edit", item);
+ this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!});
+ //update the current list
+ this.updateList(item);
+ }
+ });
+ }
+ }
+ updateList(item: Person) :void {
+ const idx = this.familyList.findIndex( x => x.id == item.id);
+ if (item.fatherId && item.fatherId > 0)
+ item.fatherName = this.getName(item.fatherId);
+ if (item.motherId && item.motherId > 0)
+ item.motherName = this.getName(item.motherId);
+ if (idx < 0)
+ {
+
+ const olist = [... this.familyList, item];
+ this.familyList = olist;
+ }
+ else
+ {
+ const oitem = this.familyList[idx];
+ oitem.firstName = item.firstName;
+ oitem.lastName = item.lastName;
+ oitem.address = item.address;
+ oitem.alive = item.alive;
+ oitem.dob = item.dob;
+ oitem.email = item.email;
+ oitem.fatherId = item.fatherId;
+ oitem.motherId = item.motherId;
+ oitem.motherName = item.motherName;
+ oitem.fatherName = item.fatherName;
+
+ }
+ }
+ deleteItem(id: number): void {
+ this.personService.deletePerson(id)
+ .pipe(take(1))
+ .subscribe({ next: result => {
+ console.log(this.msg + " deleteItem success", result);
+
+ const nlist = this.familyList.filter(d => d.id !== id);
+ this.familyList = nlist;
+ },
+ error: e => console.error(e)
+ });
+ //console.log(this.msg + "click button to delete");
+ }
+
+ ngOnDestroy() {
+ this.subscription.unsubscribe();
+ }
+}
+
diff --git a/UI/src/app/person/familylist.css b/UI/src/app/person/familylist.css
index 3db3368..1c17f21 100644
--- a/UI/src/app/person/familylist.css
+++ b/UI/src/app/person/familylist.css
@@ -1,12 +1,12 @@
-table {
- width: 100%;
-}
-
-.mat-form-field {
- font-size: 14px;
- width: 100%;
-}
-td.mat-column-edit, .mat-column-delete {
- width: 35px;
- padding-right: 2px;
-}
+table {
+ width: 100%;
+}
+
+.mat-form-field {
+ font-size: 14px;
+ width: 100%;
+}
+td.mat-column-edit, .mat-column-delete {
+ width: 35px;
+ padding-right: 2px;
+}
diff --git a/UI/src/app/person/familylist.html b/UI/src/app/person/familylist.html
index 75319aa..97e6db3 100644
--- a/UI/src/app/person/familylist.html
+++ b/UI/src/app/person/familylist.html
@@ -1,86 +1,86 @@
-
-
Person List
-
-
-
-
-
-
-
-
-
- | Last Name
- |
- First Name
- |
- Sex |
- Father
- |
- Mother
- |
-
- Edit |
-
-
-
-
- | {{user.lastName}} |
- {{user.firstName}} |
- {{user.sex}} |
- {{user.fatherName}} |
- {{user.motherName}} |
-
-
-
-
-
- |
-
-
-
-
-
-
+
+
Person List
+
+
+
+
+
+
+
+
+
+ | Last Name
+ |
+ First Name
+ |
+ Sex |
+ Father
+ |
+ Mother
+ |
+
+ Edit |
+
+
+
+
+ | {{user.lastName}} |
+ {{user.firstName}} |
+ {{user.sex}} |
+ {{user.fatherName}} |
+ {{user.motherName}} |
+
+
+
+
+
+ |
+
+
+
+
+
+
diff --git a/UI/src/app/person/familylist.ts b/UI/src/app/person/familylist.ts
index be680ff..ca33c96 100644
--- a/UI/src/app/person/familylist.ts
+++ b/UI/src/app/person/familylist.ts
@@ -1,359 +1,359 @@
-import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal, ViewChild} from '@angular/core';
-
-import { StaffView ,StaffSearch, Person } from '../models';
-
-
-import { take } from 'rxjs/operators';
-import { Subscription } from 'rxjs';
-import { Router } from '@angular/router';
-import { PersonService } from './person.service';
-import { AuthenticationService } from '../user-services';
-import { Table, TableModule } from 'primeng/table';
-import { FormsModule } from '@angular/forms';
-import { CommonModule } from '@angular/common';
-import { ButtonModule } from 'primeng/button';
-import { InputTextModule } from 'primeng/inputtext';
-import { DialogService } from 'primeng/dynamicdialog';
-import { PersonEdit } from './person.edit';
-import { ConfirmationService, MenuItem, MessageService } from 'primeng/api';
-import { IconFieldModule } from 'primeng/iconfield';
-import { InputIconModule } from 'primeng/inputicon';
-import { Menu, MenuModule } from 'primeng/menu';
-import { FamilyOrga } from './family.orga';
-
-import { saveAs } from 'file-saver';
-import * as XLSX from 'xlsx';
-
-@Component({
- selector: 'family-list',
- templateUrl: './familylist.html',
- imports:[TableModule,FormsModule,CommonModule,ButtonModule,MenuModule,
- InputTextModule,IconFieldModule,InputIconModule],
- styleUrls: ['./familylist.css'],
- providers: [DialogService]
-})
-export class FamilyList implements OnInit, OnDestroy{
-
- private subscription:Subscription = new Subscription();
- //private cd = inject(ChangeDetectorRef);
- items: MenuItem[] | undefined;
- selectedPerson!: Person;
- firstname = '';
- email = '';
- lastname = '';
- _id = -10;
- selectId = -1;
- loading = false;
- familyList = signal
([]);
- msg ="[Person component]";
- @ViewChild(Table) dt2!: Table;
- @ViewChild('rowmenu') popMenu?: Menu;
- private messageService = inject(MessageService);
- public dialogService= inject( DialogService);
- private personService= inject( PersonService);
- private confirmationService = inject(ConfirmationService);
- private cdr = inject(ChangeDetectorRef);
- private authenticationService= inject( AuthenticationService);
- private router= inject( Router);
-
- getSearchCiteria(): StaffSearch {
- let criteria:StaffSearch = {
- email: this.email,
- firstName: this.firstname,
- lastName: this.lastname,
- };
- console.log("get search citeria", criteria);
- return criteria;
-
- }
- initMenu(): void {
- this.items = [
- {
- label: 'Edit',
- icon: 'pi pi-pencil',
- command: () => {
- this.edit(this.selectId);
- }
- },
- {
- label: 'Delete',
- iconClass:'text-red-500',
- styleClass:'text-red-500',
- icon: 'pi pi-times',
- command: () => {
- this.delete(this.selectId);
- }
- },
- {
- label: 'Show Family',
- icon: 'pi pi-sitemap',
- command: () => {
- this.showChildren(this.selectId);
- }
- }
-
- ];
- }
- exportExport() : void {
- this.ExcelExport(this.familyList(), 'family_export');
- }
-
- ExcelExport(data: any, fileName:string) :void
- {
- const worksheet = XLSX.utils.json_to_sheet(data);
- const workbook = XLSX.utils.book_new();
- XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
- const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
- const blob = new Blob([excelBuffer], { type: 'application/octet-stream' });
- saveAs(blob, `${fileName}.xlsx`);
- }
-
- actionClick(id: number, event:Event): void {
- // console.log("action edit "+ id);
- this.selectId = id;
- this.popMenu!.toggle(event);
- }
- showChildren(id: number): void {
- console.log("show children of id", id);
- this.showOrganise(id);
-
-}
- handleInput(event: Event) {
- const value = (event.target as HTMLInputElement).value;
- this.dt2.filterGlobal(value, 'contains');
- }
- canSearch():boolean {
- let result = false;
- result = this.email !== "";
- result = result || this.lastname !== "";
- result = result || this.firstname !== "";
- return result;
- }
- onMenuShow(): void {
- console.log("this is show", this.selectedPerson);
- }
-
- ngOnInit(): void
- {
- this.initMenu();
- this.authenticationService.isHome = false;
- this.authenticationService.isReport = false;
- const prev = this.personService.searchCriteria;
- let goload = true;
- if (prev.lastName !== '')
- {
- this.lastname = prev.lastName;
- goload = true;
- }
- if (prev.firstName !== '')
- {
- this.firstname = prev.firstName;
- goload = true;
- }
- if (prev.email !== '')
- {
- this.email = prev.email;
- goload = true;
- }
- if (goload)
- {
- this.search();
- }
- }
- getActive(active:boolean):string {
- let result = 'false-icon pi-times-circle';
- if (active)
- result = 'true-icon pi-check-circle';
- return result;
- }
- search():void {
- const canSearch = true; // this.canSearch();
- if (canSearch)
- {
- this.loading = true;
- const criteria = this.getSearchCiteria();
- this.personService.searchCriteria = criteria;
- this.subscription.add(
- this.personService.searchPersons(criteria).subscribe( {
- next: result => {
- // console.log(this.msg + "search load Data", result);
- const familyList = result.data;
- this.familyList.set(familyList);
- this.updateParent( this.familyList());
- //this.familyList.set(familyList);
-
- console.log("the person from load", this.familyList());
- this.loading = false;
- this.cdr.detectChanges();
- },
- error: e => {
- const message = e || e.message;
- // this.toastr.error(message);
- this.loading = false;
- console.log("error ", e);
- }
- })
- );
- }
- }
- getName(id:number): string
- {
- let result ="";
- const item = this.familyList().find(x => x.id == id);
- if (item)
- result = item.lastName + " " + item.firstName;
- return result;
- }
- updateParent(list:Person[]):void {
- let i = 0;
- let item:Person;
- for (i = 0; i< list.length; i++)
- {
- item = list[i];
- if (item.fatherId && item.fatherId > 0)
- {
- item.fatherName = this.getName(item.fatherId);
- }
- if (item.motherId && item.motherId > 0)
- {
- item.motherName = this.getName(item.motherId);
- }
- }
- }
-
- delete(id: number): void {
- this.confirmationService.confirm({
-
- message: 'Do you want to delete this record?',
- header: 'Confirmation Delete',
- icon: 'pi pi-info-circle',
- rejectLabel: 'Cancel',
- rejectButtonProps: {
- label: 'Cancel',
- severity: 'secondary',
- outlined: true,
- },
- acceptButtonProps: {
- label: 'Delete',
- severity: 'danger',
- },
-
- accept: () => {
- this.deleteItem(id);
- }
- });
- }
-
- deleteItem(id: number): void {
- this.personService.deletePerson(id)
- .pipe(take(1))
- .subscribe({ next: result => {
- console.log(this.msg + " deleteItem success", result);
-
- const nlist = this.familyList().filter(d => d.id !== id);
- this.familyList.set(nlist);
- this.cdr.detectChanges();
- },
- error: e => console.error(e)
- });
- //console.log(this.msg + "click button to delete");
- }
-
- newFamily():void {
- //console.log("add new employee");
- this.personService.parentList = this.familyList();
- // this.router.navigate( ['/family/new'], { queryParams: {returnUrl:'/family' } });
- this.showEdit(this._id--);
- }
- edit(id: number) : void {
- //console.log("edit family", id);
- this.personService.parentList = this.familyList();
- // this.router.navigate( ['/family/'+id], { queryParams: {returnUrl:'/family' } });
- this.showEdit(id);
- }
- showEdit(id:number) {
- const ref = this.dialogService.open(PersonEdit, {
- data: {
- id,
- familyList: this.familyList(),
- },
- header: 'Person',
- width: '80%',
- draggable: true,
- maximizable: true
- });
- if (ref)
- {
- ref.onClose.subscribe((item: Person) => {
- if (item) {
- //console.log("after close ward edit", item);
- // this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!});
- //update the current list
- this.updateList(item);
- }
- });
- }
- }
-
- showOrganise(id:number) {
- const ref = this.dialogService.open(FamilyOrga, {
- data: {
- id,
- familyList: this.familyList(),
- },
- header: 'Family Tree and Children',
- width: '80%',
- maximizable: true,
- closable: true
- });
- if (ref)
- {
- ref.onClose.subscribe((item: Person) => {
- if (item) {
- //console.log("after close ward edit", item);
- // this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!});
- //update the current list
- //this.updateList(item);
- }
- });
- }
- }
-
- updateList(item: Person) :void {
- const list = this.familyList();
- const idx = list.findIndex( x => x.id === item.id);
- if (item.fatherId && item.fatherId > 0)
- item.fatherName = this.getName(item.fatherId);
- if (item.motherId && item.motherId > 0)
- item.motherName = this.getName(item.motherId);
- if (idx < 0)
- {
- const olist = [... list, item];
- this.familyList.set(olist);
- }
- else
- {
-
- const oitem = list[idx];
- oitem.firstName = item.firstName;
- oitem.lastName = item.lastName;
- oitem.address = item.address;
- oitem.alive = item.alive;
- oitem.dob = item.dob;
- oitem.sex = item.sex;
- oitem.email = item.email;
- oitem.fatherId = item.fatherId;
- oitem.motherId = item.motherId;
- oitem.fatherName = item.fatherName;
- oitem.motherName = item.motherName;
- this.familyList.set(list);
- }
- this.cdr.markForCheck();
- }
-
-
- ngOnDestroy() {
- this.subscription.unsubscribe();
- }
-}
-
+import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal, ViewChild} from '@angular/core';
+
+import { StaffView ,StaffSearch, Person } from '../models';
+
+
+import { take } from 'rxjs/operators';
+import { Subscription } from 'rxjs';
+import { Router } from '@angular/router';
+import { PersonService } from './person.service';
+import { AuthenticationService } from '../user-services';
+import { Table, TableModule } from 'primeng/table';
+import { FormsModule } from '@angular/forms';
+import { CommonModule } from '@angular/common';
+import { ButtonModule } from 'primeng/button';
+import { InputTextModule } from 'primeng/inputtext';
+import { DialogService } from 'primeng/dynamicdialog';
+import { PersonEdit } from './person.edit';
+import { ConfirmationService, MenuItem, MessageService } from 'primeng/api';
+import { IconFieldModule } from 'primeng/iconfield';
+import { InputIconModule } from 'primeng/inputicon';
+import { Menu, MenuModule } from 'primeng/menu';
+import { FamilyOrga } from './family.orga';
+
+import { saveAs } from 'file-saver';
+import * as XLSX from 'xlsx';
+
+@Component({
+ selector: 'family-list',
+ templateUrl: './familylist.html',
+ imports:[TableModule,FormsModule,CommonModule,ButtonModule,MenuModule,
+ InputTextModule,IconFieldModule,InputIconModule],
+ styleUrls: ['./familylist.css'],
+ providers: [DialogService]
+})
+export class FamilyList implements OnInit, OnDestroy{
+
+ private subscription:Subscription = new Subscription();
+ //private cd = inject(ChangeDetectorRef);
+ items: MenuItem[] | undefined;
+ selectedPerson!: Person;
+ firstname = '';
+ email = '';
+ lastname = '';
+ _id = -10;
+ selectId = -1;
+ loading = false;
+ familyList = signal([]);
+ msg ="[Person component]";
+ @ViewChild(Table) dt2!: Table;
+ @ViewChild('rowmenu') popMenu?: Menu;
+ private messageService = inject(MessageService);
+ public dialogService= inject( DialogService);
+ private personService= inject( PersonService);
+ private confirmationService = inject(ConfirmationService);
+ private cdr = inject(ChangeDetectorRef);
+ private authenticationService= inject( AuthenticationService);
+ private router= inject( Router);
+
+ getSearchCiteria(): StaffSearch {
+ let criteria:StaffSearch = {
+ email: this.email,
+ firstName: this.firstname,
+ lastName: this.lastname,
+ };
+ console.log("get search citeria", criteria);
+ return criteria;
+
+ }
+ initMenu(): void {
+ this.items = [
+ {
+ label: 'Edit',
+ icon: 'pi pi-pencil',
+ command: () => {
+ this.edit(this.selectId);
+ }
+ },
+ {
+ label: 'Delete',
+ iconClass:'text-red-500',
+ styleClass:'text-red-500',
+ icon: 'pi pi-times',
+ command: () => {
+ this.delete(this.selectId);
+ }
+ },
+ {
+ label: 'Show Family',
+ icon: 'pi pi-sitemap',
+ command: () => {
+ this.showChildren(this.selectId);
+ }
+ }
+
+ ];
+ }
+ exportExport() : void {
+ this.ExcelExport(this.familyList(), 'family_export');
+ }
+
+ ExcelExport(data: any, fileName:string) :void
+ {
+ const worksheet = XLSX.utils.json_to_sheet(data);
+ const workbook = XLSX.utils.book_new();
+ XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
+ const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
+ const blob = new Blob([excelBuffer], { type: 'application/octet-stream' });
+ saveAs(blob, `${fileName}.xlsx`);
+ }
+
+ actionClick(id: number, event:Event): void {
+ // console.log("action edit "+ id);
+ this.selectId = id;
+ this.popMenu!.toggle(event);
+ }
+ showChildren(id: number): void {
+ console.log("show children of id", id);
+ this.showOrganise(id);
+
+}
+ handleInput(event: Event) {
+ const value = (event.target as HTMLInputElement).value;
+ this.dt2.filterGlobal(value, 'contains');
+ }
+ canSearch():boolean {
+ let result = false;
+ result = this.email !== "";
+ result = result || this.lastname !== "";
+ result = result || this.firstname !== "";
+ return result;
+ }
+ onMenuShow(): void {
+ console.log("this is show", this.selectedPerson);
+ }
+
+ ngOnInit(): void
+ {
+ this.initMenu();
+ this.authenticationService.isHome = false;
+ this.authenticationService.isReport = false;
+ const prev = this.personService.searchCriteria;
+ let goload = true;
+ if (prev.lastName !== '')
+ {
+ this.lastname = prev.lastName;
+ goload = true;
+ }
+ if (prev.firstName !== '')
+ {
+ this.firstname = prev.firstName;
+ goload = true;
+ }
+ if (prev.email !== '')
+ {
+ this.email = prev.email;
+ goload = true;
+ }
+ if (goload)
+ {
+ this.search();
+ }
+ }
+ getActive(active:boolean):string {
+ let result = 'false-icon pi-times-circle';
+ if (active)
+ result = 'true-icon pi-check-circle';
+ return result;
+ }
+ search():void {
+ const canSearch = true; // this.canSearch();
+ if (canSearch)
+ {
+ this.loading = true;
+ const criteria = this.getSearchCiteria();
+ this.personService.searchCriteria = criteria;
+ this.subscription.add(
+ this.personService.searchPersons(criteria).subscribe( {
+ next: result => {
+ // console.log(this.msg + "search load Data", result);
+ const familyList = result.data;
+ this.familyList.set(familyList);
+ this.updateParent( this.familyList());
+ //this.familyList.set(familyList);
+
+ console.log("the person from load", this.familyList());
+ this.loading = false;
+ this.cdr.detectChanges();
+ },
+ error: e => {
+ const message = e || e.message;
+ // this.toastr.error(message);
+ this.loading = false;
+ console.log("error ", e);
+ }
+ })
+ );
+ }
+ }
+ getName(id:number): string
+ {
+ let result ="";
+ const item = this.familyList().find(x => x.id == id);
+ if (item)
+ result = item.lastName + " " + item.firstName;
+ return result;
+ }
+ updateParent(list:Person[]):void {
+ let i = 0;
+ let item:Person;
+ for (i = 0; i< list.length; i++)
+ {
+ item = list[i];
+ if (item.fatherId && item.fatherId > 0)
+ {
+ item.fatherName = this.getName(item.fatherId);
+ }
+ if (item.motherId && item.motherId > 0)
+ {
+ item.motherName = this.getName(item.motherId);
+ }
+ }
+ }
+
+ delete(id: number): void {
+ this.confirmationService.confirm({
+
+ message: 'Do you want to delete this record?',
+ header: 'Confirmation Delete',
+ icon: 'pi pi-info-circle',
+ rejectLabel: 'Cancel',
+ rejectButtonProps: {
+ label: 'Cancel',
+ severity: 'secondary',
+ outlined: true,
+ },
+ acceptButtonProps: {
+ label: 'Delete',
+ severity: 'danger',
+ },
+
+ accept: () => {
+ this.deleteItem(id);
+ }
+ });
+ }
+
+ deleteItem(id: number): void {
+ this.personService.deletePerson(id)
+ .pipe(take(1))
+ .subscribe({ next: result => {
+ console.log(this.msg + " deleteItem success", result);
+
+ const nlist = this.familyList().filter(d => d.id !== id);
+ this.familyList.set(nlist);
+ this.cdr.detectChanges();
+ },
+ error: e => console.error(e)
+ });
+ //console.log(this.msg + "click button to delete");
+ }
+
+ newFamily():void {
+ //console.log("add new employee");
+ this.personService.parentList = this.familyList();
+ // this.router.navigate( ['/family/new'], { queryParams: {returnUrl:'/family' } });
+ this.showEdit(this._id--);
+ }
+ edit(id: number) : void {
+ //console.log("edit family", id);
+ this.personService.parentList = this.familyList();
+ // this.router.navigate( ['/family/'+id], { queryParams: {returnUrl:'/family' } });
+ this.showEdit(id);
+ }
+ showEdit(id:number) {
+ const ref = this.dialogService.open(PersonEdit, {
+ data: {
+ id,
+ familyList: this.familyList(),
+ },
+ header: 'Person',
+ width: '80%',
+ draggable: true,
+ maximizable: true
+ });
+ if (ref)
+ {
+ ref.onClose.subscribe((item: Person) => {
+ if (item) {
+ //console.log("after close ward edit", item);
+ // this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!});
+ //update the current list
+ this.updateList(item);
+ }
+ });
+ }
+ }
+
+ showOrganise(id:number) {
+ const ref = this.dialogService.open(FamilyOrga, {
+ data: {
+ id,
+ familyList: this.familyList(),
+ },
+ header: 'Family Tree and Children',
+ width: '80%',
+ maximizable: true,
+ closable: true
+ });
+ if (ref)
+ {
+ ref.onClose.subscribe((item: Person) => {
+ if (item) {
+ //console.log("after close ward edit", item);
+ // this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!});
+ //update the current list
+ //this.updateList(item);
+ }
+ });
+ }
+ }
+
+ updateList(item: Person) :void {
+ const list = this.familyList();
+ const idx = list.findIndex( x => x.id === item.id);
+ if (item.fatherId && item.fatherId > 0)
+ item.fatherName = this.getName(item.fatherId);
+ if (item.motherId && item.motherId > 0)
+ item.motherName = this.getName(item.motherId);
+ if (idx < 0)
+ {
+ const olist = [... list, item];
+ this.familyList.set(olist);
+ }
+ else
+ {
+
+ const oitem = list[idx];
+ oitem.firstName = item.firstName;
+ oitem.lastName = item.lastName;
+ oitem.address = item.address;
+ oitem.alive = item.alive;
+ oitem.dob = item.dob;
+ oitem.sex = item.sex;
+ oitem.email = item.email;
+ oitem.fatherId = item.fatherId;
+ oitem.motherId = item.motherId;
+ oitem.fatherName = item.fatherName;
+ oitem.motherName = item.motherName;
+ this.familyList.set(list);
+ }
+ this.cdr.markForCheck();
+ }
+
+
+ ngOnDestroy() {
+ this.subscription.unsubscribe();
+ }
+}
+
diff --git a/UI/src/app/person/index.ts b/UI/src/app/person/index.ts
index 0455764..85b7ea8 100644
--- a/UI/src/app/person/index.ts
+++ b/UI/src/app/person/index.ts
@@ -1,5 +1,5 @@
-export * from './familylist';
-export * from './person.edit';
-export * from './family.tree';
-export * from './person.service';
-
+export * from './familylist';
+export * from './person.edit';
+export * from './family.tree';
+export * from './person.service';
+
diff --git a/UI/src/app/person/person.edit.css b/UI/src/app/person/person.edit.css
index 91b1568..1bb1e66 100644
--- a/UI/src/app/person/person.edit.css
+++ b/UI/src/app/person/person.edit.css
@@ -1,13 +1,13 @@
-.profilePhotoBorder
-{
-
- border-color: gray;
- border-width: 2px;
- border-radius: 15%;
-}
-
-.profilePhotoWH
-{
- width: 150px;
- height: 100px;
+.profilePhotoBorder
+{
+
+ border-color: gray;
+ border-width: 2px;
+ border-radius: 15%;
+}
+
+.profilePhotoWH
+{
+ width: 150px;
+ height: 100px;
}
\ No newline at end of file
diff --git a/UI/src/app/person/person.edit.html b/UI/src/app/person/person.edit.html
index 09f8fbd..4162602 100644
--- a/UI/src/app/person/person.edit.html
+++ b/UI/src/app/person/person.edit.html
@@ -1,134 +1,134 @@
-
-
-
-
+
\ No newline at end of file
diff --git a/UI/src/app/person/person.edit.ts b/UI/src/app/person/person.edit.ts
index 55b6960..5ef4769 100644
--- a/UI/src/app/person/person.edit.ts
+++ b/UI/src/app/person/person.edit.ts
@@ -1,764 +1,764 @@
-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
- });
- if (ref)
- {
- 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);
- const partnerl = this.partners();
- for (let k = 0; k < partnerl.length; k++)
- {
- vitem = partnerl[k];
- 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();
- const savePerson$ = this.personService.savePerson(container);
- this.subscription.add (savePerson$.subscribe({
- next: x => {
- if (x.statusCode >= 1)
- {
- item.id = x.data;
- this._id = item.id;
- // this.messageService.add({severity:'success', summary: 'Save user', detail: item.firstName + " " + item.lastName });
- //this.router.navigate([this.returnUrl]);
-
- const list = this.getPhotoListforSave();
- console.log("the person after save photo, person", list, item);
- 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());
- this.cdr.markForCheck();
- });
-}
-
-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
- });
- if (ref)
- {
- 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
- });
- if (ref)
- {
- ref.onClose.subscribe((ritem: any) => {
- const item = ritem.list;
- const deleteIds = ritem.deleteIds;
- if (item) {
- console.log("after close " + title, item);
- if (item.length > 0)
- {
- 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 < 1)
- {
- 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', item.id.toString());
- console.log("savephotolist person id", item.id, item);
- const personPhoto$ = this.personService.savePersonPhotoList(formData);
- this.subscription.add(personPhoto$.subscribe(
- {
- next: x => {
- if (x.statusCode == 1) {
- this.ref.close(item);
- }
- 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();
-}
-}
+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
+ });
+ if (ref)
+ {
+ 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);
+ const partnerl = this.partners();
+ for (let k = 0; k < partnerl.length; k++)
+ {
+ vitem = partnerl[k];
+ 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();
+ const savePerson$ = this.personService.savePerson(container);
+ this.subscription.add (savePerson$.subscribe({
+ next: x => {
+ if (x.statusCode >= 1)
+ {
+ item.id = x.data;
+ this._id = item.id;
+ // this.messageService.add({severity:'success', summary: 'Save user', detail: item.firstName + " " + item.lastName });
+ //this.router.navigate([this.returnUrl]);
+
+ const list = this.getPhotoListforSave();
+ console.log("the person after save photo, person", list, item);
+ 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());
+ this.cdr.markForCheck();
+ });
+}
+
+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
+ });
+ if (ref)
+ {
+ 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
+ });
+ if (ref)
+ {
+ ref.onClose.subscribe((ritem: any) => {
+ const item = ritem.list;
+ const deleteIds = ritem.deleteIds;
+ if (item) {
+ console.log("after close " + title, item);
+ if (item.length > 0)
+ {
+ 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 < 1)
+ {
+ 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', item.id.toString());
+ console.log("savephotolist person id", item.id, item);
+ const personPhoto$ = this.personService.savePersonPhotoList(formData);
+ this.subscription.add(personPhoto$.subscribe(
+ {
+ next: x => {
+ if (x.statusCode == 1) {
+ this.ref.close(item);
+ }
+ 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();
+}
+}
diff --git a/UI/src/app/person/person.service.ts b/UI/src/app/person/person.service.ts
index e008cdd..28c7642 100644
--- a/UI/src/app/person/person.service.ts
+++ b/UI/src/app/person/person.service.ts
@@ -1,109 +1,109 @@
-
-import { Injectable } from '@angular/core';
-import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
-import { Observable } from 'rxjs';
-import { Staff,StaffSearch, StaffView, ResultModel,ConfigureUrl,ResetPassword, Person, PersonContainer, RelationShip } from '../models';
-import { AppSettingService } from '../shares';
-import { TreeNode } from 'primeng/api';
-
-@Injectable({ providedIn: 'root' })
-export class PersonService {
- public searchCriteria: StaffSearch;
- public parentList: Person[] =[];
- constructor(private http: HttpClient,
-
- private appSetting :AppSettingService
- ) {
- this.searchCriteria = {
- email:'',
- firstName: '',
- lastName:''
- };
-
- }
- searchPersons(criteria: StaffSearch): Observable> {
- let config = { headers : { 'Content-Type': 'application/json' } };
- const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl + "/SearchPerson";
- return this.http.post>(baseUrl, criteria, config);
- }
-
- loadPersonById(id:number): Observable> {
- const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
- /*
- const params = new HttpParams().set("id", ""+id);
- const headers = new HttpHeaders().set('Content-Type', 'application/json');
- const options = {
- headers: headers,
- params: params
- };
- */
-
- return this.http.get>(baseUrl + "/GetById/" + id);
- }
- loadPersonFamily(id: number): Observable> {
- const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
- return this.http.get>(baseUrl + "/GetByPersonFamily/" + id);
- }
-
- loadPersonFamilyTree(useFather: boolean, useMother: boolean): Observable> {
- const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
- const data = {useFather,useMother};
- return this.http.post>(baseUrl + "/GetFamilyTreeBy", data);
- }
-
- loadChildrenById(fatherId:number, motherId: number): Observable> {
- const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
- const data = {fatherId,motherId};
-
- return this.http.post>(baseUrl + "/GetChildress/" ,data);
- }
- loadRelationshipById(personId:number): Observable> {
- const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.relationShipUrl;
- return this.http.get>(baseUrl + "/GetByPersonId/" +personId);
- }
- savePerson(data:PersonContainer): Observable> { //insert Adminuser
- let config = { headers : { 'Content-Type': 'application/json' } };
- console.log("save family", data);
- const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl +"/SavePerson";
- return this.http.post>(baseUrl, data, config);
- }
-
- deletePerson(id:number): Observable>{
- const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
- const data = {id};
- return this.http.post>(baseUrl + "/DeleteById" ,data);
- }
- uploadFile(data: any): Observable> {
- const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.personUrl + "/UploadImage";
- return this.http.post>(baseUrl, data);
- }
- downloadFile(imageName: string): Observable> {
- const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/DownloadFile";
- const data = {
- fileName: imageName
- };
- return this.http.post>(baseUrl, data);
- }
- deleteUploadFile(data: any): Observable> {
- //data ={filename};
- const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.personUrl + "/DeleteUploadFile";
- return this.http.post>(baseUrl, data);
- }
- deletePersonPhotoFile(id: number): Observable> {
- const data ={id, fileName:''};
- const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/DeletePersonPhoto";
- return this.http.post>(baseUrl, data);
- }
- savePersonPhotoList(data:FormData): Observable> {
- const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/SavePersonPhoto";
- return this.http.post>(baseUrl, data);
- }
- downloadPersonPhoto(id: number): Observable> {
- const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/DownloadPersonPhoto";
- const data = {
- id,
- fileName: ''
- };
- return this.http.post>(baseUrl, data);
- }
-}
+
+import { Injectable } from '@angular/core';
+import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
+import { Observable } from 'rxjs';
+import { Staff,StaffSearch, StaffView, ResultModel,ConfigureUrl,ResetPassword, Person, PersonContainer, RelationShip } from '../models';
+import { AppSettingService } from '../shares';
+import { TreeNode } from 'primeng/api';
+
+@Injectable({ providedIn: 'root' })
+export class PersonService {
+ public searchCriteria: StaffSearch;
+ public parentList: Person[] =[];
+ constructor(private http: HttpClient,
+
+ private appSetting :AppSettingService
+ ) {
+ this.searchCriteria = {
+ email:'',
+ firstName: '',
+ lastName:''
+ };
+
+ }
+ searchPersons(criteria: StaffSearch): Observable> {
+ let config = { headers : { 'Content-Type': 'application/json' } };
+ const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl + "/SearchPerson";
+ return this.http.post>(baseUrl, criteria, config);
+ }
+
+ loadPersonById(id:number): Observable> {
+ const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
+ /*
+ const params = new HttpParams().set("id", ""+id);
+ const headers = new HttpHeaders().set('Content-Type', 'application/json');
+ const options = {
+ headers: headers,
+ params: params
+ };
+ */
+
+ return this.http.get>(baseUrl + "/GetById/" + id);
+ }
+ loadPersonFamily(id: number): Observable> {
+ const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
+ return this.http.get>(baseUrl + "/GetByPersonFamily/" + id);
+ }
+
+ loadPersonFamilyTree(useFather: boolean, useMother: boolean): Observable> {
+ const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
+ const data = {useFather,useMother};
+ return this.http.post>(baseUrl + "/GetFamilyTreeBy", data);
+ }
+
+ loadChildrenById(fatherId:number, motherId: number): Observable> {
+ const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
+ const data = {fatherId,motherId};
+
+ return this.http.post>(baseUrl + "/GetChildress/" ,data);
+ }
+ loadRelationshipById(personId:number): Observable> {
+ const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.relationShipUrl;
+ return this.http.get>(baseUrl + "/GetByPersonId/" +personId);
+ }
+ savePerson(data:PersonContainer): Observable> { //insert Adminuser
+ let config = { headers : { 'Content-Type': 'application/json' } };
+ console.log("save family", data);
+ const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl +"/SavePerson";
+ return this.http.post>(baseUrl, data, config);
+ }
+
+ deletePerson(id:number): Observable>{
+ const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl;
+ const data = {id};
+ return this.http.post>(baseUrl + "/DeleteById" ,data);
+ }
+ uploadFile(data: any): Observable> {
+ const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.personUrl + "/UploadImage";
+ return this.http.post>(baseUrl, data);
+ }
+ downloadFile(imageName: string): Observable> {
+ const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/DownloadFile";
+ const data = {
+ fileName: imageName
+ };
+ return this.http.post>(baseUrl, data);
+ }
+ deleteUploadFile(data: any): Observable> {
+ //data ={filename};
+ const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.personUrl + "/DeleteUploadFile";
+ return this.http.post>(baseUrl, data);
+ }
+ deletePersonPhotoFile(id: number): Observable> {
+ const data ={id, fileName:''};
+ const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/DeletePersonPhoto";
+ return this.http.post>(baseUrl, data);
+ }
+ savePersonPhotoList(data:FormData): Observable> {
+ const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/SavePersonPhoto";
+ return this.http.post>(baseUrl, data);
+ }
+ downloadPersonPhoto(id: number): Observable> {
+ const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/DownloadPersonPhoto";
+ const data = {
+ id,
+ fileName: ''
+ };
+ return this.http.post>(baseUrl, data);
+ }
+}
diff --git a/UI/src/app/pickperson/image.display.html b/UI/src/app/pickperson/image.display.html
index a84eca5..780b338 100644
--- a/UI/src/app/pickperson/image.display.html
+++ b/UI/src/app/pickperson/image.display.html
@@ -1,5 +1,5 @@
-
-
-
+
![Person Image]()
+
+
\ No newline at end of file
diff --git a/UI/src/app/pickperson/image.display.ts b/UI/src/app/pickperson/image.display.ts
index fc8f6de..d6e2760 100644
--- a/UI/src/app/pickperson/image.display.ts
+++ b/UI/src/app/pickperson/image.display.ts
@@ -1,64 +1,64 @@
-import { Component, inject, OnDestroy, OnInit, signal } from '@angular/core';
-import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
-import { AppSettingService } from '../shares';
-import { PersonService } from '../person';
-import { Subscription } from 'rxjs';
-import { ActivatedRoute } from '@angular/router';
-import { CommonModule } from '@angular/common';
-import { ButtonModule } from 'primeng/button';
-import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
-
-@Component({
- selector: 'app-image-display',
- imports:[CommonModule, ButtonModule],
- templateUrl: './image.display.html',
- styleUrls: ['./image.display.css']
-})
-export class ImageDisplayComponent implements OnInit, OnDestroy {
- //base64ImageString: string = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; // Example Base64 (a tiny red dot PNG)
- //imageDataUrl: SafeResourceUrl ={};
- imageDataUrl = signal
("");
- private personService = inject(PersonService);
- private sanitizer = inject(DomSanitizer);
- //private route = inject(ActivatedRoute);
- public ref = inject(DynamicDialogRef);
- public config = inject(DynamicDialogConfig);
- private subscription:Subscription = new Subscription();
-
- ngOnInit(): void {
-
- const imageName = this.config.data.imageName;
- this.loadImage(imageName);
-
- }
- loadImage(fileName: string| null): void {
- const download = this.personService.downloadFile(fileName!);
- this.subscription.add(download.subscribe({
- next: x => {
- if (x.statusCode == 1)
- {
- this.display(x.data);
- console.log("this is show image" , this.imageDataUrl(), x);
- }
- else
- {
- console.log("error in download in api ", x.message);
- }
- },
- error: e => console.error("error in download image", e)
- }));
- }
- display(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);
- }
- ngOnDestroy(): void {
- this.subscription.unsubscribe();
- }
- close() :void {
- this.ref.close(null);
- }
+import { Component, inject, OnDestroy, OnInit, signal } from '@angular/core';
+import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
+import { AppSettingService } from '../shares';
+import { PersonService } from '../person';
+import { Subscription } from 'rxjs';
+import { ActivatedRoute } from '@angular/router';
+import { CommonModule } from '@angular/common';
+import { ButtonModule } from 'primeng/button';
+import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
+
+@Component({
+ selector: 'app-image-display',
+ imports:[CommonModule, ButtonModule],
+ templateUrl: './image.display.html',
+ styleUrls: ['./image.display.css']
+})
+export class ImageDisplayComponent implements OnInit, OnDestroy {
+ //base64ImageString: string = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; // Example Base64 (a tiny red dot PNG)
+ //imageDataUrl: SafeResourceUrl ={};
+ imageDataUrl = signal("");
+ private personService = inject(PersonService);
+ private sanitizer = inject(DomSanitizer);
+ //private route = inject(ActivatedRoute);
+ public ref = inject(DynamicDialogRef);
+ public config = inject(DynamicDialogConfig);
+ private subscription:Subscription = new Subscription();
+
+ ngOnInit(): void {
+
+ const imageName = this.config.data.imageName;
+ this.loadImage(imageName);
+
+ }
+ loadImage(fileName: string| null): void {
+ const download = this.personService.downloadFile(fileName!);
+ this.subscription.add(download.subscribe({
+ next: x => {
+ if (x.statusCode == 1)
+ {
+ this.display(x.data);
+ console.log("this is show image" , this.imageDataUrl(), x);
+ }
+ else
+ {
+ console.log("error in download in api ", x.message);
+ }
+ },
+ error: e => console.error("error in download image", e)
+ }));
+ }
+ display(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);
+ }
+ ngOnDestroy(): void {
+ this.subscription.unsubscribe();
+ }
+ close() :void {
+ this.ref.close(null);
+ }
}
\ No newline at end of file
diff --git a/UI/src/app/pickperson/pickperson.html b/UI/src/app/pickperson/pickperson.html
index d56bbb3..47cb992 100644
--- a/UI/src/app/pickperson/pickperson.html
+++ b/UI/src/app/pickperson/pickperson.html
@@ -1,47 +1,47 @@
-
-
-
-
-
-
-
- | Last Name
- |
- First Name
- |
- Sex
- |
- |
-
-
-
-
- | {{user.lastName}} |
- {{user.firstName}} |
- {{user.sex}} |
- |
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+ | Last Name
+ |
+ First Name
+ |
+ Sex
+ |
+ |
+
+
+
+
+ | {{user.lastName}} |
+ {{user.firstName}} |
+ {{user.sex}} |
+ |
+
+
+
+
+
+
+
+
+
diff --git a/UI/src/app/pickperson/pickperson.ts b/UI/src/app/pickperson/pickperson.ts
index 3b5753d..cf1a373 100644
--- a/UI/src/app/pickperson/pickperson.ts
+++ b/UI/src/app/pickperson/pickperson.ts
@@ -1,53 +1,53 @@
-import { CommonModule } from '@angular/common';
-import { Component, OnDestroy, OnInit, signal, ViewChild } from '@angular/core';
-import { FormsModule } from '@angular/forms';
-import { Table, TableModule } from 'primeng/table';
-import { Person } from '../models';
-import { IconFieldModule } from 'primeng/iconfield';
-import { InputIconModule } from 'primeng/inputicon';
-import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
-import { ButtonModule } from 'primeng/button';
-import { InputTextModule } from 'primeng/inputtext';
-
-@Component({
- selector: 'app-pickperson',
- imports: [TableModule, ButtonModule,CommonModule, InputTextModule, FormsModule,IconFieldModule,InputIconModule],
- templateUrl: './pickperson.html',
- styleUrl: './pickperson.css'
-})
-export class Pickperson implements OnInit, OnDestroy{
- @ViewChild(Table) dt2!: Table;
- constructor(public ref: DynamicDialogRef, public config: DynamicDialogConfig)
- {
-
- }
- loading = false;
- familyList = signal([]);
- selectedPerson!: Person;
- handleInput(event: Event) {
- const value = (event.target as HTMLInputElement).value;
- this.dt2.filterGlobal(value, 'contains');
- }
-
- edit(id: number): void {
-
- }
- cancel(e:Event):void {
- e.preventDefault();
- this.ref.close(null);
- }
- select(e:Event):void {
- e.preventDefault();
- this.ref.close(this.selectedPerson);
- }
-
- ngOnDestroy(): void {
-
- }
- ngOnInit(): void {
- console.log("pick person the familyList", this.config);
- this.familyList.set(this.config.data.familyList);
-
- }
-
-}
+import { CommonModule } from '@angular/common';
+import { Component, OnDestroy, OnInit, signal, ViewChild } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { Table, TableModule } from 'primeng/table';
+import { Person } from '../models';
+import { IconFieldModule } from 'primeng/iconfield';
+import { InputIconModule } from 'primeng/inputicon';
+import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
+import { ButtonModule } from 'primeng/button';
+import { InputTextModule } from 'primeng/inputtext';
+
+@Component({
+ selector: 'app-pickperson',
+ imports: [TableModule, ButtonModule,CommonModule, InputTextModule, FormsModule,IconFieldModule,InputIconModule],
+ templateUrl: './pickperson.html',
+ styleUrl: './pickperson.css'
+})
+export class Pickperson implements OnInit, OnDestroy{
+ @ViewChild(Table) dt2!: Table;
+ constructor(public ref: DynamicDialogRef, public config: DynamicDialogConfig)
+ {
+
+ }
+ loading = false;
+ familyList = signal([]);
+ selectedPerson!: Person;
+ handleInput(event: Event) {
+ const value = (event.target as HTMLInputElement).value;
+ this.dt2.filterGlobal(value, 'contains');
+ }
+
+ edit(id: number): void {
+
+ }
+ cancel(e:Event):void {
+ e.preventDefault();
+ this.ref.close(null);
+ }
+ select(e:Event):void {
+ e.preventDefault();
+ this.ref.close(this.selectedPerson);
+ }
+
+ ngOnDestroy(): void {
+
+ }
+ ngOnInit(): void {
+ console.log("pick person the familyList", this.config);
+ this.familyList.set(this.config.data.familyList);
+
+ }
+
+}
diff --git a/UI/src/app/route-guard/auth.guard.ts b/UI/src/app/route-guard/auth.guard.ts
index fd14db4..e66a150 100644
--- a/UI/src/app/route-guard/auth.guard.ts
+++ b/UI/src/app/route-guard/auth.guard.ts
@@ -1,52 +1,52 @@
-import { Injectable } from '@angular/core';
-import { Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
-import { Utils } from '../shares';
-import {AuthenticationService} from '../user-services/authentication.service';
-@Injectable({ providedIn: 'root' })
-export class AuthGuard {
- msg ="[AuthGuard] ";
- constructor(private router: Router,
- private authService :AuthenticationService ) { }
-
- canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
- const roleAllowed = route.data['roleAllowed'];
- const user = Utils.getCurrentUser();
- const userRole = Utils.getUserRole(user);
- let hasLoggedIn = Utils.getIsAuth();
- //console.log(this.msg + " the userlogin ", user, hasLoggedIn);
- if (hasLoggedIn) {
-
- if (roleAllowed != undefined)
- {
- //it number toString
- // console.log("[auth-guard] ther roleAllow ", roleAllowed);
- // console.log("[auth-guard] before roleAllow ", roleAllowed,userRole);
- if (roleAllowed.indexOf(userRole.toString()) != -1) {
- // console.log("[auth-guard] pass roleAllow ", roleAllowed,userRole);
- return true;
- }
- else {
- this.authService.logout();
- this.router.navigate(['/login']);
- return false;
- }
- }
- else //if no role define just return hasLoggedIn
- {
- return hasLoggedIn;
- }
- }
- else
- {
- // this.authService.logout();
- this.router.navigate(['/login']);
- return false;
- }
-
- // not logged in so redirect to login page with the return url
- //this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
- this.authService.logout();
- return false;
- }
-
-}
+import { Injectable } from '@angular/core';
+import { Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
+import { Utils } from '../shares';
+import {AuthenticationService} from '../user-services/authentication.service';
+@Injectable({ providedIn: 'root' })
+export class AuthGuard {
+ msg ="[AuthGuard] ";
+ constructor(private router: Router,
+ private authService :AuthenticationService ) { }
+
+ canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
+ const roleAllowed = route.data['roleAllowed'];
+ const user = Utils.getCurrentUser();
+ const userRole = Utils.getUserRole(user);
+ let hasLoggedIn = Utils.getIsAuth();
+ //console.log(this.msg + " the userlogin ", user, hasLoggedIn);
+ if (hasLoggedIn) {
+
+ if (roleAllowed != undefined)
+ {
+ //it number toString
+ // console.log("[auth-guard] ther roleAllow ", roleAllowed);
+ // console.log("[auth-guard] before roleAllow ", roleAllowed,userRole);
+ if (roleAllowed.indexOf(userRole.toString()) != -1) {
+ // console.log("[auth-guard] pass roleAllow ", roleAllowed,userRole);
+ return true;
+ }
+ else {
+ this.authService.logout();
+ this.router.navigate(['/login']);
+ return false;
+ }
+ }
+ else //if no role define just return hasLoggedIn
+ {
+ return hasLoggedIn;
+ }
+ }
+ else
+ {
+ // this.authService.logout();
+ this.router.navigate(['/login']);
+ return false;
+ }
+
+ // not logged in so redirect to login page with the return url
+ //this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
+ this.authService.logout();
+ return false;
+ }
+
+}
diff --git a/UI/src/app/route-guard/index.ts b/UI/src/app/route-guard/index.ts
index b41e34a..1860171 100644
--- a/UI/src/app/route-guard/index.ts
+++ b/UI/src/app/route-guard/index.ts
@@ -1 +1 @@
-export * from './auth.guard';
+export * from './auth.guard';
diff --git a/UI/src/app/shares/CommonUtilities.ts b/UI/src/app/shares/CommonUtilities.ts
index 46f5788..51093c3 100644
--- a/UI/src/app/shares/CommonUtilities.ts
+++ b/UI/src/app/shares/CommonUtilities.ts
@@ -1,54 +1,54 @@
-import { FormControl, FormGroup } from '@angular/forms';
-
-export class CommonUtilities {
- /** Display the blob */
- static displayFileInNewTab(response:any, defaultFileName:string) {
- const contentType = response.headers.get('content-type');
- const blob = new Blob([response.body], { type: contentType });
- const fileName = this.getFileName(response,defaultFileName);
- const nav = (window.navigator as any);
- if (nav.msSaveOrOpenBlob) {
- nav.msSaveOrOpenBlob(blob, fileName);
- } else {
- const data = window.URL.createObjectURL(blob);
- const link = document.createElement('a');
- link.href = data;
- link.target = '_blank';
- link.click();
- }
- }
- /*** Download the blob ***/
- static downloadFile(response:any, defaultFileName:string) {
- const contentType = response.headers.get('content-type');
- const blob = new Blob([response.body], { type: contentType });
- const fileName = this.getFileName(response,defaultFileName);
- const nav = (window.navigator as any);
- if (typeof nav.msSaveBlob === 'function')
- {
- console.log("download the report saveblob");
- nav.msSaveBlob(blob, fileName);
- console.log("download the report saveblob done");
- }
- else {
- const link = document.createElement('a');
- link.href = window.URL.createObjectURL(blob);
- console.log("no msSaveBlob download the report saveblob done");
- //link.download? = fileName;
- link.download = fileName;
- link.click();
- }
- }
- /** Get the attachment file name */
- static getFileName(response:any, defaultFileName:string) {
- const contentDis = response.headers.get('content-disposition');
- let fileName =defaultFileName;
- if (contentDis && contentDis.indexOf('attachment') !== -1) {
- const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
- const matches = filenameRegex.exec(contentDis);
- if (matches != null && matches[1]) {
- fileName = matches[1].replace(/['"]/g, '');
- }
- }
- return fileName;
- }
+import { FormControl, FormGroup } from '@angular/forms';
+
+export class CommonUtilities {
+ /** Display the blob */
+ static displayFileInNewTab(response:any, defaultFileName:string) {
+ const contentType = response.headers.get('content-type');
+ const blob = new Blob([response.body], { type: contentType });
+ const fileName = this.getFileName(response,defaultFileName);
+ const nav = (window.navigator as any);
+ if (nav.msSaveOrOpenBlob) {
+ nav.msSaveOrOpenBlob(blob, fileName);
+ } else {
+ const data = window.URL.createObjectURL(blob);
+ const link = document.createElement('a');
+ link.href = data;
+ link.target = '_blank';
+ link.click();
+ }
+ }
+ /*** Download the blob ***/
+ static downloadFile(response:any, defaultFileName:string) {
+ const contentType = response.headers.get('content-type');
+ const blob = new Blob([response.body], { type: contentType });
+ const fileName = this.getFileName(response,defaultFileName);
+ const nav = (window.navigator as any);
+ if (typeof nav.msSaveBlob === 'function')
+ {
+ console.log("download the report saveblob");
+ nav.msSaveBlob(blob, fileName);
+ console.log("download the report saveblob done");
+ }
+ else {
+ const link = document.createElement('a');
+ link.href = window.URL.createObjectURL(blob);
+ console.log("no msSaveBlob download the report saveblob done");
+ //link.download? = fileName;
+ link.download = fileName;
+ link.click();
+ }
+ }
+ /** Get the attachment file name */
+ static getFileName(response:any, defaultFileName:string) {
+ const contentDis = response.headers.get('content-disposition');
+ let fileName =defaultFileName;
+ if (contentDis && contentDis.indexOf('attachment') !== -1) {
+ const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
+ const matches = filenameRegex.exec(contentDis);
+ if (matches != null && matches[1]) {
+ fileName = matches[1].replace(/['"]/g, '');
+ }
+ }
+ return fileName;
+ }
}
\ No newline at end of file
diff --git a/UI/src/app/shares/appsetting.ts b/UI/src/app/shares/appsetting.ts
index aecf15d..683bd19 100644
--- a/UI/src/app/shares/appsetting.ts
+++ b/UI/src/app/shares/appsetting.ts
@@ -1,77 +1,77 @@
-import { inject, Injectable } from '@angular/core';
-import { HttpClient } from '@angular/common/http';
-import { firstValueFrom, Observable } from 'rxjs';
-export function initializeApp_old(appInitService: AppSettingService) {
- return (): Promise => {
- return appInitService.loadAppSetting_p();
- }
-
-};
-export function initializeApp() {
- return (): Promise => {
- return inject(AppSettingService).loadAppSetting_p();
- }
- // return () => inject(AppSettingService).loadAppSetting();
-};
-
-@Injectable({ providedIn: 'root' })
-export class AppSettingService {
- private _appsetting: any;
- constructor(private http: HttpClient
- ) { }
-
- public loadAppSetting_p(): Promise {
- const source$ = this.http.get("config/appsetting.json")
- const rs$ = firstValueFrom(source$)
- .then(x => {
- this._appsetting = x;
- console.log("[AppSettingService] assign", x);
- });
- return rs$;
- }
- public loadAppSetting(): Observable {
- const source$ = this.http.get("config/appsetting.json")
- return source$;
- }
- set appSetting(value: any) {
- this._appsetting = value;
- }
- get appSetting(): any {
- return this._appsetting;
- }
-
-}
-/* angular 19 new one in app.config.ts
- provideAppInitializer(initializeApp()),
-
-/* module put this
-to use it
-in app.module.ts
-add
-1)
- providers: [
- { provide : APP_INITIALIZER, multi : true, deps : [AppSettingService],
- useFactory: initializeApp
- },
- ]
-
-2)
-other this one work too.
-providers: [
-{
- provide : APP_INITIALIZER,
- multi : true,
- deps : [AppSettingService],
- useFactory : (appConfigService : AppSettingService) => () => appConfigService.loadAppSetting()
-}
-]
-
-to use it calling something like this
-export class TestComponent {
- public test1ServiceUrl: string;
-
- constructor(public configService: AppConfigService) {
- this.test1ServiceUrl = this.configService.appSetting.test1ServiceUrl;
- }
-}
+import { inject, Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { firstValueFrom, Observable } from 'rxjs';
+export function initializeApp_old(appInitService: AppSettingService) {
+ return (): Promise => {
+ return appInitService.loadAppSetting_p();
+ }
+
+};
+export function initializeApp() {
+ return (): Promise => {
+ return inject(AppSettingService).loadAppSetting_p();
+ }
+ // return () => inject(AppSettingService).loadAppSetting();
+};
+
+@Injectable({ providedIn: 'root' })
+export class AppSettingService {
+ private _appsetting: any;
+ constructor(private http: HttpClient
+ ) { }
+
+ public loadAppSetting_p(): Promise {
+ const source$ = this.http.get("config/appsetting.json")
+ const rs$ = firstValueFrom(source$)
+ .then(x => {
+ this._appsetting = x;
+ console.log("[AppSettingService] assign", x);
+ });
+ return rs$;
+ }
+ public loadAppSetting(): Observable {
+ const source$ = this.http.get("config/appsetting.json")
+ return source$;
+ }
+ set appSetting(value: any) {
+ this._appsetting = value;
+ }
+ get appSetting(): any {
+ return this._appsetting;
+ }
+
+}
+/* angular 19 new one in app.config.ts
+ provideAppInitializer(initializeApp()),
+
+/* module put this
+to use it
+in app.module.ts
+add
+1)
+ providers: [
+ { provide : APP_INITIALIZER, multi : true, deps : [AppSettingService],
+ useFactory: initializeApp
+ },
+ ]
+
+2)
+other this one work too.
+providers: [
+{
+ provide : APP_INITIALIZER,
+ multi : true,
+ deps : [AppSettingService],
+ useFactory : (appConfigService : AppSettingService) => () => appConfigService.loadAppSetting()
+}
+]
+
+to use it calling something like this
+export class TestComponent {
+ public test1ServiceUrl: string;
+
+ constructor(public configService: AppConfigService) {
+ this.test1ServiceUrl = this.configService.appSetting.test1ServiceUrl;
+ }
+}
*/
\ No newline at end of file
diff --git a/UI/src/app/shares/error.interceptor.ts b/UI/src/app/shares/error.interceptor.ts
index 5379593..e6e293d 100644
--- a/UI/src/app/shares/error.interceptor.ts
+++ b/UI/src/app/shares/error.interceptor.ts
@@ -1,97 +1,97 @@
-import { Injectable, inject } from '@angular/core';
-import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpInterceptorFn, HttpHandlerFn } from '@angular/common/http';
-import { Observable, throwError } from 'rxjs';
-import { catchError } from 'rxjs/operators';
-
-import { AuthenticationService } from '../user-services';
-//import { ToastrService } from 'ngx-toastr';
-import { NavigationExtras, Router } from '@angular/router';
-/*
-@Injectable()
-export class ErrorInterceptor implements HttpInterceptor {
- //private authenticationService: AuthenticationService;
- constructor(
- private router: Router,
- // private toastr: ToastrService,
- ) { }
-
- intercept(request: HttpRequest, next: HttpHandler): Observable> {
- // console.log("I am intercept to catch error status = 401...", request);
- return next.handle(request).pipe(catchError(err => {
- /* no use yet
- if (err) {
- switch(err.status) {
- case 400:
- if (err.error.errors) {
- const modalStateErrors = [];
- for (const key in err.error.errors) {
- if (err.error.errors[key]) {
- modalStateErrors.push(err.error.errors[key]);
- }
- }
- throw modalStateErrors;
- } else {
- this.toastr.error(err.statusText, err.status);
- }
- break;
- case 401:
- this.toastr.error(err.statusText, err.status);
- break;
- case 404:
- this.router.navigateByUrl('/not-found');
- break;
- case 500:
- const navigationExtras: NavigationExtras = {state:{error:err.error}};
- this.router.navigateByUrl('/server-error', navigationExtras);
- break;
- default:
- this.toastr.error('Something unexpected went wrong');
- console.log("this error interceptor",err);
- break;
- }
- }
- */
-/*
- if (err.status === 401) {
- // auto logout if 401 response returned from api Unauthorized
- // this.authenticationService.logout();
- //location.reload();
- const error = err.error.message || err.statusText;
- console.log("After I Get Error status = 401...", error, err);
- return throwError(error);
- }
- else {
- console.log("I Get Error .", err);
- return throwError(err);
- }
- }))
-}
-}
-
-*/
-//new one
-//https://blog.ninja-squad.com/2022/11/09/angular-http-in-standalone-applications/
-////////////////
-export const ErrorInterceptor: HttpInterceptorFn = (req: HttpRequest, next: HttpHandlerFn): Observable> => {
- const authenticationService = inject(AuthenticationService);
-
- // logger.log(`Request is on its way to ${req.url}`);
- return next(req).pipe(catchError(err => {
- if (err.status === 401) {
- // auto logout if 401 response returned from api Unauthorized
- // this.authenticationService.logout();
- //location.reload();
- const error = err.error.message || err.statusText;
- // console.log("After I Get Error status = 401...", error, err);
- return throwError(() => error);
- }
- else {
- console.log("I Get Error .", err);
- return throwError(() => err);
- }
-
- }));
-}
-
-////////////////
-
+import { Injectable, inject } from '@angular/core';
+import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpInterceptorFn, HttpHandlerFn } from '@angular/common/http';
+import { Observable, throwError } from 'rxjs';
+import { catchError } from 'rxjs/operators';
+
+import { AuthenticationService } from '../user-services';
+//import { ToastrService } from 'ngx-toastr';
+import { NavigationExtras, Router } from '@angular/router';
+/*
+@Injectable()
+export class ErrorInterceptor implements HttpInterceptor {
+ //private authenticationService: AuthenticationService;
+ constructor(
+ private router: Router,
+ // private toastr: ToastrService,
+ ) { }
+
+ intercept(request: HttpRequest, next: HttpHandler): Observable> {
+ // console.log("I am intercept to catch error status = 401...", request);
+ return next.handle(request).pipe(catchError(err => {
+ /* no use yet
+ if (err) {
+ switch(err.status) {
+ case 400:
+ if (err.error.errors) {
+ const modalStateErrors = [];
+ for (const key in err.error.errors) {
+ if (err.error.errors[key]) {
+ modalStateErrors.push(err.error.errors[key]);
+ }
+ }
+ throw modalStateErrors;
+ } else {
+ this.toastr.error(err.statusText, err.status);
+ }
+ break;
+ case 401:
+ this.toastr.error(err.statusText, err.status);
+ break;
+ case 404:
+ this.router.navigateByUrl('/not-found');
+ break;
+ case 500:
+ const navigationExtras: NavigationExtras = {state:{error:err.error}};
+ this.router.navigateByUrl('/server-error', navigationExtras);
+ break;
+ default:
+ this.toastr.error('Something unexpected went wrong');
+ console.log("this error interceptor",err);
+ break;
+ }
+ }
+ */
+/*
+ if (err.status === 401) {
+ // auto logout if 401 response returned from api Unauthorized
+ // this.authenticationService.logout();
+ //location.reload();
+ const error = err.error.message || err.statusText;
+ console.log("After I Get Error status = 401...", error, err);
+ return throwError(error);
+ }
+ else {
+ console.log("I Get Error .", err);
+ return throwError(err);
+ }
+ }))
+}
+}
+
+*/
+//new one
+//https://blog.ninja-squad.com/2022/11/09/angular-http-in-standalone-applications/
+////////////////
+export const ErrorInterceptor: HttpInterceptorFn = (req: HttpRequest, next: HttpHandlerFn): Observable> => {
+ const authenticationService = inject(AuthenticationService);
+
+ // logger.log(`Request is on its way to ${req.url}`);
+ return next(req).pipe(catchError(err => {
+ if (err.status === 401) {
+ // auto logout if 401 response returned from api Unauthorized
+ // this.authenticationService.logout();
+ //location.reload();
+ const error = err.error.message || err.statusText;
+ // console.log("After I Get Error status = 401...", error, err);
+ return throwError(() => error);
+ }
+ else {
+ console.log("I Get Error .", err);
+ return throwError(() => err);
+ }
+
+ }));
+}
+
+////////////////
+
diff --git a/UI/src/app/shares/httpfile.service.ts b/UI/src/app/shares/httpfile.service.ts
index aa32074..13cf92f 100644
--- a/UI/src/app/shares/httpfile.service.ts
+++ b/UI/src/app/shares/httpfile.service.ts
@@ -1,115 +1,115 @@
-import { HttpClient, HttpErrorResponse, HttpParams } from '@angular/common/http';
-import { Injectable } from '@angular/core';
-import { Router } from '@angular/router';
-import { Observable, throwError } from 'rxjs';
-import { catchError } from 'rxjs/operators';
-import { ConfigureUrl } from '../models';
-import { AppSettingService } from './appsetting';
-
-
-@Injectable({
- providedIn: 'root'
-})
-export class HttpUtility {
- private baseApiUrl: string = "";
- constructor(private http: HttpClient,
- private appSetting :AppSettingService,
- private router: Router) {
- }
- /**
- * getFile
- * @param url
- */
- public getFileAsText(url: string): Observable {
- return this.http
- .get(this.getApiUrl(url), { responseType: 'blob' as 'text' })
- .pipe(catchError(e => this.handleError(e)));
- }
- /**
- * getFile
- * @param url
- */
- public getFile(url: string): Observable {
- return this.http
- .get(this.getApiUrl(url), { responseType: 'blob' as 'json' })
- .pipe(catchError(e => this.handleError(e)));
- }
-
- /**
- * handleError
- * @param response
- */
- private handleError(errorResponse: HttpErrorResponse) {
- // in a real world app, we may send the server to some remote logging infrastructure
- // instead of just logging it to the console
- return throwError(errorResponse);
- }
- //get excel file zip file etc ..
- /*
- allowedToDisplay = [
- this.mimeConstant.png,
- this.mimeConstant.jpeg,
- this.mimeConstant.jpg,
- this.mimeConstant.gif,
- this.mimeConstant.txt,
- this.mimeConstant.pdf
- ];
- */
- /* how to use it
- this.http.getFileResponse(`Person/GetPhoto?recordId=${personId}`).pipe(
- catchError(err => {
- this.snackBar.error(err || this.constants.error_Getting_photo);
- return EMPTY;
- }),
- finalize(() => {
- this.spinner.hide();
- })).subscribe(response => {
- if (response) {
- if (isDownload) {
- CommonUtilities.downloadFile(response);
- } else {
- if (response.body && response.body.type) {
- if (this.allowedToDisplay.includes(response.body.type)) {
- CommonUtilities.displayFileInNewTab(response);
- } else {
- this.temporaryFile = response;
- this.downloadWarning = true;
- }
- }
- }
- }
- });
- */
- public getFileResponse(url: string, data?: any): Observable {
- if (data) {
- return this.http.post(this.getApiUrl(url),
- data,
- { responseType: 'blob', observe: 'response' })
- .pipe(catchError(e => this.handleError(e)));
-
- }
- return this.http
- .get(this.getApiUrl(url), { responseType: 'blob', observe: 'response' })
- .pipe(catchError(e => this.handleError(e)));
- }
-
- public getFileAsPDF(url: string, data: any): Observable {
- let updateURL;
- if (data) {
- updateURL = url + data;
- } else {
- updateURL = url;
- }
- return this.http
- .get(this.getApiUrl(updateURL), { responseType: 'blob' as 'text', observe: 'response' })
- .pipe(catchError(e => this.handleError(e)));
- }
- /**
- * getApiUrl
- * @param url
- */
- private getApiUrl(url:string) {
- const baseUrl = this.appSetting.appSetting.baseUrl;
- return baseUrl + "/"+ url;
- }
+import { HttpClient, HttpErrorResponse, HttpParams } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { Router } from '@angular/router';
+import { Observable, throwError } from 'rxjs';
+import { catchError } from 'rxjs/operators';
+import { ConfigureUrl } from '../models';
+import { AppSettingService } from './appsetting';
+
+
+@Injectable({
+ providedIn: 'root'
+})
+export class HttpUtility {
+ private baseApiUrl: string = "";
+ constructor(private http: HttpClient,
+ private appSetting :AppSettingService,
+ private router: Router) {
+ }
+ /**
+ * getFile
+ * @param url
+ */
+ public getFileAsText(url: string): Observable {
+ return this.http
+ .get(this.getApiUrl(url), { responseType: 'blob' as 'text' })
+ .pipe(catchError(e => this.handleError(e)));
+ }
+ /**
+ * getFile
+ * @param url
+ */
+ public getFile(url: string): Observable {
+ return this.http
+ .get(this.getApiUrl(url), { responseType: 'blob' as 'json' })
+ .pipe(catchError(e => this.handleError(e)));
+ }
+
+ /**
+ * handleError
+ * @param response
+ */
+ private handleError(errorResponse: HttpErrorResponse) {
+ // in a real world app, we may send the server to some remote logging infrastructure
+ // instead of just logging it to the console
+ return throwError(errorResponse);
+ }
+ //get excel file zip file etc ..
+ /*
+ allowedToDisplay = [
+ this.mimeConstant.png,
+ this.mimeConstant.jpeg,
+ this.mimeConstant.jpg,
+ this.mimeConstant.gif,
+ this.mimeConstant.txt,
+ this.mimeConstant.pdf
+ ];
+ */
+ /* how to use it
+ this.http.getFileResponse(`Person/GetPhoto?recordId=${personId}`).pipe(
+ catchError(err => {
+ this.snackBar.error(err || this.constants.error_Getting_photo);
+ return EMPTY;
+ }),
+ finalize(() => {
+ this.spinner.hide();
+ })).subscribe(response => {
+ if (response) {
+ if (isDownload) {
+ CommonUtilities.downloadFile(response);
+ } else {
+ if (response.body && response.body.type) {
+ if (this.allowedToDisplay.includes(response.body.type)) {
+ CommonUtilities.displayFileInNewTab(response);
+ } else {
+ this.temporaryFile = response;
+ this.downloadWarning = true;
+ }
+ }
+ }
+ }
+ });
+ */
+ public getFileResponse(url: string, data?: any): Observable {
+ if (data) {
+ return this.http.post(this.getApiUrl(url),
+ data,
+ { responseType: 'blob', observe: 'response' })
+ .pipe(catchError(e => this.handleError(e)));
+
+ }
+ return this.http
+ .get(this.getApiUrl(url), { responseType: 'blob', observe: 'response' })
+ .pipe(catchError(e => this.handleError(e)));
+ }
+
+ public getFileAsPDF(url: string, data: any): Observable {
+ let updateURL;
+ if (data) {
+ updateURL = url + data;
+ } else {
+ updateURL = url;
+ }
+ return this.http
+ .get(this.getApiUrl(updateURL), { responseType: 'blob' as 'text', observe: 'response' })
+ .pipe(catchError(e => this.handleError(e)));
+ }
+ /**
+ * getApiUrl
+ * @param url
+ */
+ private getApiUrl(url:string) {
+ const baseUrl = this.appSetting.appSetting.baseUrl;
+ return baseUrl + "/"+ url;
+ }
}
\ No newline at end of file
diff --git a/UI/src/app/shares/index.ts b/UI/src/app/shares/index.ts
index 31c1fea..0db2649 100644
--- a/UI/src/app/shares/index.ts
+++ b/UI/src/app/shares/index.ts
@@ -1,8 +1,8 @@
-export * from './utils';
-export {initializeApp, AppSettingService} from './appsetting';
-export * from './error.interceptor';
-export * from './jwt.interceptor';
-export * from './lookup.service';
-export * from './httpfile.service';
-export * from './CommonUtilities';
+export * from './utils';
+export {initializeApp, AppSettingService} from './appsetting';
+export * from './error.interceptor';
+export * from './jwt.interceptor';
+export * from './lookup.service';
+export * from './httpfile.service';
+export * from './CommonUtilities';
export * from './timeinput';
\ No newline at end of file
diff --git a/UI/src/app/shares/inputtext.txt b/UI/src/app/shares/inputtext.txt
index 0415954..776a95e 100644
--- a/UI/src/app/shares/inputtext.txt
+++ b/UI/src/app/shares/inputtext.txt
@@ -1,1554 +1,1554 @@
-import { CommonModule } from '@angular/common';
-import {
- AfterContentInit,
- booleanAttribute,
- ChangeDetectionStrategy,
- Component,
- ContentChild,
- ContentChildren,
- ElementRef,
- EventEmitter,
- forwardRef,
- HostBinding,
- inject,
- Injector,
- Input,
- NgModule,
- numberAttribute,
- OnChanges,
- OnInit,
- Output,
- QueryList,
- SimpleChanges,
- TemplateRef,
- ViewChild,
- ViewEncapsulation
-} from '@angular/core';
-import { ControlValueAccessor, NG_VALUE_ACCESSOR, NgControl } from '@angular/forms';
-import { getSelection } from '@primeuix/utils';
-import { PrimeTemplate, SharedModule } from 'primeng/api';
-import { AutoFocus } from 'primeng/autofocus';
-import { BaseComponent } from 'primeng/basecomponent';
-import { AngleDownIcon, AngleUpIcon, TimesIcon } from 'primeng/icons';
-import { InputText } from 'primeng/inputtext';
-import { Nullable } from 'primeng/ts-helpers';
-import { InputNumberInputEvent } from './inputnumber.interface';
-import { InputNumberStyle } from './style/inputnumberstyle';
-
-export const INPUTNUMBER_VALUE_ACCESSOR: any = {
- provide: NG_VALUE_ACCESSOR,
- useExisting: forwardRef(() => InputNumber),
- multi: true
-};
-/**
- * InputNumber is an input component to provide numerical input.
- * @group Components
- */
-@Component({
- selector: 'p-inputNumber, p-inputnumber, p-input-number',
- standalone: true,
- imports: [CommonModule, InputText, AutoFocus, TimesIcon, AngleUpIcon, AngleDownIcon, SharedModule],
- template: `
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- `,
- changeDetection: ChangeDetectionStrategy.OnPush,
- providers: [INPUTNUMBER_VALUE_ACCESSOR, InputNumberStyle],
- encapsulation: ViewEncapsulation.None,
- host: {
- '[attr.data-pc-name]': "'inputnumber'",
- '[attr.data-pc-section]': "'root'",
- '[class]': 'hostClass'
- }
-})
-export class InputNumber extends BaseComponent implements OnInit, AfterContentInit, OnChanges, ControlValueAccessor {
- /**
- * Displays spinner buttons.
- * @group Props
- */
- @Input({ transform: booleanAttribute }) showButtons: boolean = false;
- /**
- * Whether to format the value.
- * @group Props
- */
- @Input({ transform: booleanAttribute }) format: boolean = true;
- /**
- * Layout of the buttons, valid values are "stacked" (default), "horizontal" and "vertical".
- * @group Props
- */
- @Input() buttonLayout: string = 'stacked';
- /**
- * Identifier of the focus input to match a label defined for the component.
- * @group Props
- */
- @Input() inputId: string | undefined;
- /**
- * Style class of the component.
- * @group Props
- */
- @Input() styleClass: string | undefined;
- /**
- * Inline style of the component.
- * @group Props
- */
- @Input() style: { [klass: string]: any } | null | undefined;
- /**
- * Advisory information to display on input.
- * @group Props
- */
- @Input() placeholder: string | undefined;
- /**
- * Defines the size of the component.
- * @group Props
- */
- @Input() size: 'large' | 'small';
- /**
- * Maximum number of character allows in the input field.
- * @group Props
- */
- @Input({ transform: numberAttribute }) maxlength: number | undefined;
- /**
- * Specifies tab order of the element.
- * @group Props
- */
- @Input({ transform: numberAttribute }) tabindex: number | undefined;
- /**
- * Title text of the input text.
- * @group Props
- */
- @Input() title: string | undefined;
- /**
- * Specifies one or more IDs in the DOM that labels the input field.
- * @group Props
- */
- @Input() ariaLabelledBy: string | undefined;
- /**
- * Specifies one or more IDs in the DOM that describes the input field.
- * @group Props
- */
- @Input() ariaDescribedBy: string | undefined;
- /**
- * Used to define a string that labels the input element.
- * @group Props
- */
- @Input() ariaLabel: string | undefined;
- /**
- * Used to indicate that user input is required on an element before a form can be submitted.
- * @group Props
- */
- @Input({ transform: booleanAttribute }) ariaRequired: boolean | undefined;
- /**
- * Name of the input field.
- * @group Props
- */
- @Input() name: string | undefined;
- /**
- * Indicates that whether the input field is required.
- * @group Props
- */
- @Input({ transform: booleanAttribute }) required: boolean | undefined;
- /**
- * Used to define a string that autocomplete attribute the current element.
- * @group Props
- */
- @Input() autocomplete: string | undefined;
- /**
- * Mininum boundary value.
- * @group Props
- */
- @Input({ transform: numberAttribute }) min: number | undefined;
- /**
- * Maximum boundary value.
- * @group Props
- */
- @Input({ transform: numberAttribute }) max: number | undefined;
- /**
- * Style class of the increment button.
- * @group Props
- */
- @Input() incrementButtonClass: string | undefined;
- /**
- * Style class of the decrement button.
- * @group Props
- */
- @Input() decrementButtonClass: string | undefined;
- /**
- * Style class of the increment button.
- * @group Props
- */
- @Input() incrementButtonIcon: string | undefined;
- /**
- * Style class of the decrement button.
- * @group Props
- */
- @Input() decrementButtonIcon: string | undefined;
- /**
- * When present, it specifies that an input field is read-only.
- * @group Props
- */
- @Input({ transform: booleanAttribute }) readonly: boolean = false;
- /**
- * Step factor to increment/decrement the value.
- * @group Props
- */
- @Input({ transform: numberAttribute }) step: number = 1;
- /**
- * Determines whether the input field is empty.
- * @group Props
- */
- @Input({ transform: booleanAttribute }) allowEmpty: boolean = true;
- /**
- * Locale to be used in formatting.
- * @group Props
- */
- @Input() locale: string | undefined;
- /**
- * The locale matching algorithm to use. Possible values are "lookup" and "best fit"; the default is "best fit". See Locale Negotiation for details.
- * @group Props
- */
- @Input() localeMatcher: any;
- /**
- * Defines the behavior of the component, valid values are "decimal" and "currency".
- * @group Props
- */
- @Input() mode: string | any = 'decimal';
- /**
- * The currency to use in currency formatting. Possible values are the ISO 4217 currency codes, such as "USD" for the US dollar, "EUR" for the euro, or "CNY" for the Chinese RMB. There is no default value; if the style is "currency", the currency property must be provided.
- * @group Props
- */
- @Input() currency: string | undefined;
- /**
- * How to display the currency in currency formatting. Possible values are "symbol" to use a localized currency symbol such as €, ü"code" to use the ISO currency code, "name" to use a localized currency name such as "dollar"; the default is "symbol".
- * @group Props
- */
- @Input() currencyDisplay: string | undefined | any;
- /**
- * Whether to use grouping separators, such as thousands separators or thousand/lakh/crore separators.
- * @group Props
- */
- @Input({ transform: booleanAttribute }) useGrouping: boolean = true;
- /**
- * Specifies the input variant of the component.
- * @group Props
- */
- @Input() variant: 'filled' | 'outlined';
- /**
- * The minimum number of fraction digits to use. Possible values are from 0 to 20; the default for plain number and percent formatting is 0; the default for currency formatting is the number of minor unit digits provided by the ISO 4217 currency code list (2 if the list doesn't provide that information).
- * @group Props
- */
- @Input({ transform: (value: unknown) => numberAttribute(value, null) }) minFractionDigits: number | undefined;
- /**
- * The maximum number of fraction digits to use. Possible values are from 0 to 20; the default for plain number formatting is the larger of minimumFractionDigits and 3; the default for currency formatting is the larger of minimumFractionDigits and the number of minor unit digits provided by the ISO 4217 currency code list (2 if the list doesn't provide that information).
- * @group Props
- */
- @Input({ transform: (value: unknown) => numberAttribute(value, null) }) maxFractionDigits: number | undefined;
- /**
- * Text to display before the value.
- * @group Props
- */
- @Input() prefix: string | undefined;
- /**
- * Text to display after the value.
- * @group Props
- */
- @Input() suffix: string | undefined;
- /**
- * Inline style of the input field.
- * @group Props
- */
- @Input() inputStyle: any;
- /**
- * Style class of the input field.
- * @group Props
- */
- @Input() inputStyleClass: string | undefined;
- /**
- * When enabled, a clear icon is displayed to clear the value.
- * @group Props
- */
- @Input({ transform: booleanAttribute }) showClear: boolean = false;
- /**
- * When present, it specifies that the component should automatically get focus on load.
- * @group Props
- */
- @Input({ transform: booleanAttribute }) autofocus: boolean | undefined;
- /**
- * When present, it specifies that the element should be disabled.
- * @group Props
- */
- @Input() get disabled(): boolean | undefined {
- return this._disabled;
- }
- set disabled(disabled: boolean | undefined) {
- if (disabled) this.focused = false;
-
- this._disabled = disabled;
-
- if (this.timer) this.clearTimer();
- }
- /**
- * Spans 100% width of the container when enabled.
- * @group Props
- */
- @Input({ transform: booleanAttribute }) fluid: boolean = false;
- /**
- * Callback to invoke on input.
- * @param {InputNumberInputEvent} event - Custom input event.
- * @group Emits
- */
- @Output() onInput: EventEmitter = new EventEmitter();
- /**
- * Callback to invoke when the component receives focus.
- * @param {Event} event - Browser event.
- * @group Emits
- */
- @Output() onFocus: EventEmitter = new EventEmitter();
- /**
- * Callback to invoke when the component loses focus.
- * @param {Event} event - Browser event.
- * @group Emits
- */
- @Output() onBlur: EventEmitter = new EventEmitter();
- /**
- * Callback to invoke on input key press.
- * @param {KeyboardEvent} event - Keyboard event.
- * @group Emits
- */
- @Output() onKeyDown: EventEmitter = new EventEmitter();
- /**
- * Callback to invoke when clear token is clicked.
- * @group Emits
- */
- @Output() onClear: EventEmitter = new EventEmitter();
-
- /**
- * Template of the clear icon.
- * @group Templates
- */
- @ContentChild('clearicon', { descendants: false }) clearIconTemplate: Nullable>;
- /**
- * Template of the increment button icon.
- * @group Templates
- */
- @ContentChild('incrementbuttonicon', { descendants: false }) incrementButtonIconTemplate: Nullable>;
-
- /**
- * Template of the decrement button icon.
- * @group Templates
- */
- @ContentChild('decrementbuttonicon', { descendants: false }) decrementButtonIconTemplate: Nullable>;
-
- @ContentChildren(PrimeTemplate) templates!: QueryList;
-
- @ViewChild('input') input!: ElementRef;
-
- _clearIconTemplate: TemplateRef | undefined;
-
- _incrementButtonIconTemplate: TemplateRef | undefined;
-
- _decrementButtonIconTemplate: TemplateRef | undefined;
-
- value: Nullable;
-
- onModelChange: Function = () => {};
-
- onModelTouched: Function = () => {};
-
- focused: Nullable;
-
- initialized: Nullable;
-
- groupChar: string = '';
-
- prefixChar: string = '';
-
- suffixChar: string = '';
-
- isSpecialChar: Nullable;
-
- timer: any;
-
- lastValue: Nullable;
-
- _numeral: any;
-
- numberFormat: any;
-
- _decimal: any;
-
- _decimalChar: string;
-
- _group: any;
-
- _minusSign: any;
-
- _currency: Nullable;
-
- _prefix: Nullable;
-
- _suffix: Nullable;
-
- _index: number | any;
-
- _disabled: boolean | undefined;
-
- _componentStyle = inject(InputNumberStyle);
-
- private ngControl: NgControl | null = null;
-
- get _rootClass() {
- return this._componentStyle.classes.root({ instance: this });
- }
-
- get hasFluid() {
- const nativeElement = this.el.nativeElement;
- const fluidComponent = nativeElement.closest('p-fluid');
- return this.fluid || !!fluidComponent;
- }
-
- get _incrementButtonClass() {
- return this._componentStyle.classes.incrementButton({ instance: this });
- }
-
- get _decrementButtonClass() {
- return this._componentStyle.classes.decrementButton({ instance: this });
- }
-
- constructor(public readonly injector: Injector) {
- super();
- }
-
- ngOnChanges(simpleChange: SimpleChanges) {
- super.ngOnChanges(simpleChange);
- const props = ['locale', 'localeMatcher', 'mode', 'currency', 'currencyDisplay', 'useGrouping', 'minFractionDigits', 'maxFractionDigits', 'prefix', 'suffix'];
- if (props.some((p) => !!simpleChange[p])) {
- this.updateConstructParser();
- }
- }
-
- get hostClass(): string {
- return [
- 'p-inputnumber p-component p-inputwrapper',
- this.styleClass,
- this.filled || this.allowEmpty === false ? 'p-inputwrapper-filled' : '',
- this.focused ? 'p-inputwrapper-focus' : '',
- this.showButtons && this.buttonLayout === 'stacked' ? 'p-inputnumber-stacked' : '',
- this.showButtons && this.buttonLayout === 'horizontal' ? 'p-inputnumber-horizontal' : '',
- this.showButtons && this.buttonLayout === 'vertical' ? 'p-inputnumber-vertical' : '',
- this.hasFluid ? 'p-inputnumber-fluid' : ''
- ]
- .filter((cls) => !!cls)
- .join(' ');
- }
-
- @HostBinding('style') get hostStyle(): any {
- return this.style;
- }
-
- ngOnInit() {
- super.ngOnInit();
-
- this.ngControl = this.injector.get(NgControl, null, { optional: true });
-
- this.constructParser();
-
- this.initialized = true;
- }
-
- ngAfterContentInit() {
- this.templates.forEach((item) => {
- switch (item.getType()) {
- case 'clearicon':
- this._clearIconTemplate = item.template;
- break;
-
- case 'incrementbuttonicon':
- this._incrementButtonIconTemplate = item.template;
- break;
-
- case 'decrementbuttonicon':
- this._decrementButtonIconTemplate = item.template;
- break;
- }
- });
- }
-
- getOptions() {
- return {
- localeMatcher: this.localeMatcher,
- style: this.mode,
- currency: this.currency,
- currencyDisplay: this.currencyDisplay,
- useGrouping: this.useGrouping,
- minimumFractionDigits: this.minFractionDigits ?? undefined,
- maximumFractionDigits: this.maxFractionDigits ?? undefined
- };
- }
-
- constructParser() {
- this.numberFormat = new Intl.NumberFormat(this.locale, this.getOptions());
- const numerals = [...new Intl.NumberFormat(this.locale, { useGrouping: false }).format(9876543210)].reverse();
- const index = new Map(numerals.map((d, i) => [d, i]));
- this._numeral = new RegExp(`[${numerals.join('')}]`, 'g');
- this._group = this.getGroupingExpression();
- this._minusSign = this.getMinusSignExpression();
- this._currency = this.getCurrencyExpression();
- this._decimal = this.getDecimalExpression();
- this._decimalChar = this.getDecimalChar();
- this._suffix = this.getSuffixExpression();
- this._prefix = this.getPrefixExpression();
- this._index = (d: any) => index.get(d);
- }
-
- updateConstructParser() {
- if (this.initialized) {
- this.constructParser();
- }
- }
-
- escapeRegExp(text: string): string {
- return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
- }
-
- getDecimalExpression(): RegExp {
- const decimalChar = this.getDecimalChar();
- return new RegExp(`[${decimalChar}]`, 'g');
- }
- getDecimalChar(): string {
- const formatter = new Intl.NumberFormat(this.locale, { ...this.getOptions(), useGrouping: false });
- return formatter
- .format(1.1)
- .replace(this._currency as RegExp | string, '')
- .trim()
- .replace(this._numeral, '');
- }
-
- getGroupingExpression(): RegExp {
- const formatter = new Intl.NumberFormat(this.locale, { useGrouping: true });
- this.groupChar = formatter.format(1000000).trim().replace(this._numeral, '').charAt(0);
- return new RegExp(`[${this.groupChar}]`, 'g');
- }
-
- getMinusSignExpression(): RegExp {
- const formatter = new Intl.NumberFormat(this.locale, { useGrouping: false });
- return new RegExp(`[${formatter.format(-1).trim().replace(this._numeral, '')}]`, 'g');
- }
-
- getCurrencyExpression(): RegExp {
- if (this.currency) {
- const formatter = new Intl.NumberFormat(this.locale, {
- style: 'currency',
- currency: this.currency,
- currencyDisplay: this.currencyDisplay,
- minimumFractionDigits: 0,
- maximumFractionDigits: 0
- });
- return new RegExp(`[${formatter.format(1).replace(/\s/g, '').replace(this._numeral, '').replace(this._group, '')}]`, 'g');
- }
-
- return new RegExp(`[]`, 'g');
- }
-
- getPrefixExpression(): RegExp {
- if (this.prefix) {
- this.prefixChar = this.prefix;
- } else {
- const formatter = new Intl.NumberFormat(this.locale, {
- style: this.mode,
- currency: this.currency,
- currencyDisplay: this.currencyDisplay
- });
- this.prefixChar = formatter.format(1).split('1')[0];
- }
-
- return new RegExp(`${this.escapeRegExp(this.prefixChar || '')}`, 'g');
- }
-
- getSuffixExpression(): RegExp {
- if (this.suffix) {
- this.suffixChar = this.suffix;
- } else {
- const formatter = new Intl.NumberFormat(this.locale, {
- style: this.mode,
- currency: this.currency,
- currencyDisplay: this.currencyDisplay,
- minimumFractionDigits: 0,
- maximumFractionDigits: 0
- });
- this.suffixChar = formatter.format(1).split('1')[1];
- }
-
- return new RegExp(`${this.escapeRegExp(this.suffixChar || '')}`, 'g');
- }
-
- formatValue(value: any) {
- if (value != null) {
- if (value === '-') {
- // Minus sign
- return value;
- }
-
- if (this.format) {
- let formatter = new Intl.NumberFormat(this.locale, this.getOptions());
- let formattedValue = formatter.format(value);
-
- if (this.prefix && value != this.prefix) {
- formattedValue = this.prefix + formattedValue;
- }
-
- if (this.suffix && value != this.suffix) {
- formattedValue = formattedValue + this.suffix;
- }
-
- return formattedValue;
- }
-
- return value.toString();
- }
-
- return '';
- }
-
- parseValue(text: any) {
- const suffixRegex = new RegExp(this._suffix, '');
- const prefixRegex = new RegExp(this._prefix, '');
- const currencyRegex = new RegExp(this._currency, '');
-
- let filteredText = text
- .replace(suffixRegex, '')
- .replace(prefixRegex, '')
- .trim()
- .replace(/\s/g, '')
- .replace(currencyRegex, '')
- .replace(this._group, '')
- .replace(this._minusSign, '-')
- .replace(this._decimal, '.')
- .replace(this._numeral, this._index);
-
- if (filteredText) {
- if (filteredText === '-')
- // Minus sign
- return filteredText;
-
- let parsedValue = +filteredText;
- return isNaN(parsedValue) ? null : parsedValue;
- }
-
- return null;
- }
-
- repeat(event: Event, interval: number | null, dir: number) {
- if (this.readonly) {
- return;
- }
-
- let i = interval || 500;
-
- this.clearTimer();
- this.timer = setTimeout(() => {
- this.repeat(event, 40, dir);
- }, i);
-
- this.spin(event, dir);
- }
-
- spin(event: Event, dir: number) {
- let step = this.step * dir;
- let currentValue = this.parseValue(this.input?.nativeElement.value) || 0;
- let newValue = this.validateValue((currentValue as number) + step);
- if (this.maxlength && this.maxlength < this.formatValue(newValue).length) {
- return;
- }
- this.updateInput(newValue, null, 'spin', null);
- this.updateModel(event, newValue);
-
- this.handleOnInput(event, currentValue, newValue);
- }
-
- clear() {
- this.value = null;
- this.onModelChange(this.value);
- this.onClear.emit();
- }
-
- onUpButtonMouseDown(event: MouseEvent) {
- if (event.button === 2) {
- this.clearTimer();
- return;
- }
-
- if (!this.disabled) {
- this.input?.nativeElement.focus();
- this.repeat(event, null, 1);
- event.preventDefault();
- }
- }
-
- onUpButtonMouseUp() {
- if (!this.disabled) {
- this.clearTimer();
- }
- }
-
- onUpButtonMouseLeave() {
- if (!this.disabled) {
- this.clearTimer();
- }
- }
-
- onUpButtonKeyDown(event: KeyboardEvent) {
- if (event.keyCode === 32 || event.keyCode === 13) {
- this.repeat(event, null, 1);
- }
- }
-
- onUpButtonKeyUp() {
- if (!this.disabled) {
- this.clearTimer();
- }
- }
-
- onDownButtonMouseDown(event: MouseEvent) {
- if (event.button === 2) {
- this.clearTimer();
- return;
- }
- if (!this.disabled) {
- this.input?.nativeElement.focus();
- this.repeat(event, null, -1);
- event.preventDefault();
- }
- }
-
- onDownButtonMouseUp() {
- if (!this.disabled) {
- this.clearTimer();
- }
- }
-
- onDownButtonMouseLeave() {
- if (!this.disabled) {
- this.clearTimer();
- }
- }
-
- onDownButtonKeyUp() {
- if (!this.disabled) {
- this.clearTimer();
- }
- }
-
- onDownButtonKeyDown(event: KeyboardEvent) {
- if (event.keyCode === 32 || event.keyCode === 13) {
- this.repeat(event, null, -1);
- }
- }
-
- onUserInput(event: Event) {
- if (this.readonly) {
- return;
- }
-
- if (this.isSpecialChar) {
- (event.target as HTMLInputElement).value = this.lastValue as string;
- }
- this.isSpecialChar = false;
- }
-
- onInputKeyDown(event: KeyboardEvent) {
- if (this.readonly) {
- return;
- }
-
- this.lastValue = (event.target as HTMLInputElement).value;
- if ((event as KeyboardEvent).shiftKey || (event as KeyboardEvent).altKey) {
- this.isSpecialChar = true;
- return;
- }
-
- let selectionStart = (event.target as HTMLInputElement).selectionStart as number;
- let selectionEnd = (event.target as HTMLInputElement).selectionEnd as number;
- let inputValue = (event.target as HTMLInputElement).value as string;
- let newValueStr = null;
-
- if (event.altKey) {
- event.preventDefault();
- }
-
- switch (event.key) {
- case 'ArrowUp':
- this.spin(event, 1);
- event.preventDefault();
- break;
-
- case 'ArrowDown':
- this.spin(event, -1);
- event.preventDefault();
- break;
-
- case 'ArrowLeft':
- for (let index = selectionStart; index <= inputValue.length; index++) {
- const previousCharIndex = index === 0 ? 0 : index - 1;
- if (this.isNumeralChar(inputValue.charAt(previousCharIndex))) {
- this.input.nativeElement.setSelectionRange(index, index);
- break;
- }
- }
- break;
-
- case 'ArrowRight':
- for (let index = selectionEnd; index >= 0; index--) {
- if (this.isNumeralChar(inputValue.charAt(index))) {
- this.input.nativeElement.setSelectionRange(index, index);
- break;
- }
- }
- break;
-
- case 'Tab':
- case 'Enter':
- newValueStr = this.validateValue(this.parseValue(this.input.nativeElement.value));
- this.input.nativeElement.value = this.formatValue(newValueStr);
- this.input.nativeElement.setAttribute('aria-valuenow', newValueStr);
- this.updateModel(event, newValueStr);
- break;
-
- case 'Backspace': {
- event.preventDefault();
-
- if (selectionStart === selectionEnd) {
- if ((selectionStart == 1 && this.prefix) || (selectionStart == inputValue.length && this.suffix)) {
- break;
- }
-
- const deleteChar = inputValue.charAt(selectionStart - 1);
- const { decimalCharIndex, decimalCharIndexWithoutPrefix } = this.getDecimalCharIndexes(inputValue);
-
- if (this.isNumeralChar(deleteChar)) {
- const decimalLength = this.getDecimalLength(inputValue);
-
- if (this._group.test(deleteChar)) {
- this._group.lastIndex = 0;
- newValueStr = inputValue.slice(0, selectionStart - 2) + inputValue.slice(selectionStart - 1);
- } else if (this._decimal.test(deleteChar)) {
- this._decimal.lastIndex = 0;
-
- if (decimalLength) {
- this.input?.nativeElement.setSelectionRange(selectionStart - 1, selectionStart - 1);
- } else {
- newValueStr = inputValue.slice(0, selectionStart - 1) + inputValue.slice(selectionStart);
- }
- } else if (decimalCharIndex > 0 && selectionStart > decimalCharIndex) {
- const insertedText = this.isDecimalMode() && (this.minFractionDigits || 0) < decimalLength ? '' : '0';
- newValueStr = inputValue.slice(0, selectionStart - 1) + insertedText + inputValue.slice(selectionStart);
- } else if (decimalCharIndexWithoutPrefix === 1) {
- newValueStr = inputValue.slice(0, selectionStart - 1) + '0' + inputValue.slice(selectionStart);
- newValueStr = (this.parseValue(newValueStr) as number) > 0 ? newValueStr : '';
- } else {
- newValueStr = inputValue.slice(0, selectionStart - 1) + inputValue.slice(selectionStart);
- }
- } else if (this.mode === 'currency' && deleteChar.search(this._currency) != -1) {
- newValueStr = inputValue.slice(1);
- }
-
- this.updateValue(event, newValueStr, null, 'delete-single');
- } else {
- newValueStr = this.deleteRange(inputValue, selectionStart, selectionEnd);
- this.updateValue(event, newValueStr, null, 'delete-range');
- }
-
- break;
- }
-
- case 'Delete':
- event.preventDefault();
-
- if (selectionStart === selectionEnd) {
- if ((selectionStart == 0 && this.prefix) || (selectionStart == inputValue.length - 1 && this.suffix)) {
- break;
- }
- const deleteChar = inputValue.charAt(selectionStart);
- const { decimalCharIndex, decimalCharIndexWithoutPrefix } = this.getDecimalCharIndexes(inputValue);
-
- if (this.isNumeralChar(deleteChar)) {
- const decimalLength = this.getDecimalLength(inputValue);
-
- if (this._group.test(deleteChar)) {
- this._group.lastIndex = 0;
- newValueStr = inputValue.slice(0, selectionStart) + inputValue.slice(selectionStart + 2);
- } else if (this._decimal.test(deleteChar)) {
- this._decimal.lastIndex = 0;
-
- if (decimalLength) {
- this.input?.nativeElement.setSelectionRange(selectionStart + 1, selectionStart + 1);
- } else {
- newValueStr = inputValue.slice(0, selectionStart) + inputValue.slice(selectionStart + 1);
- }
- } else if (decimalCharIndex > 0 && selectionStart > decimalCharIndex) {
- const insertedText = this.isDecimalMode() && (this.minFractionDigits || 0) < decimalLength ? '' : '0';
- newValueStr = inputValue.slice(0, selectionStart) + insertedText + inputValue.slice(selectionStart + 1);
- } else if (decimalCharIndexWithoutPrefix === 1) {
- newValueStr = inputValue.slice(0, selectionStart) + '0' + inputValue.slice(selectionStart + 1);
- newValueStr = (this.parseValue(newValueStr) as number) > 0 ? newValueStr : '';
- } else {
- newValueStr = inputValue.slice(0, selectionStart) + inputValue.slice(selectionStart + 1);
- }
- }
-
- this.updateValue(event, newValueStr as string, null, 'delete-back-single');
- } else {
- newValueStr = this.deleteRange(inputValue, selectionStart, selectionEnd);
- this.updateValue(event, newValueStr, null, 'delete-range');
- }
- break;
-
- case 'Home':
- if (this.min) {
- this.updateModel(event, this.min);
- event.preventDefault();
- }
- break;
-
- case 'End':
- if (this.max) {
- this.updateModel(event, this.max);
- event.preventDefault();
- }
- break;
-
- default:
- break;
- }
-
- this.onKeyDown.emit(event);
- }
-
- onInputKeyPress(event: KeyboardEvent) {
- if (this.readonly) {
- return;
- }
-
- let code = event.which || event.keyCode;
- let char = String.fromCharCode(code);
- let isDecimalSign = this.isDecimalSign(char);
- const isMinusSign = this.isMinusSign(char);
-
- if (code != 13) {
- event.preventDefault();
- }
- if (!isDecimalSign && event.code === 'NumpadDecimal') {
- isDecimalSign = true;
- char = this._decimalChar;
- code = char.charCodeAt(0);
- }
- const { value, selectionStart, selectionEnd } = this.input.nativeElement;
- const newValue = this.parseValue(value + char);
- const newValueStr = newValue != null ? newValue.toString() : '';
- const selectedValue = value.substring(selectionStart, selectionEnd);
- const selectedValueParsed = this.parseValue(selectedValue);
- const selectedValueStr = selectedValueParsed != null ? selectedValueParsed.toString() : '';
-
- if (selectionStart !== selectionEnd && selectedValueStr.length > 0) {
- this.insert(event, char, { isDecimalSign, isMinusSign });
- return;
- }
-
- if (this.maxlength && newValueStr.length > this.maxlength) {
- return;
- }
-
- if ((48 <= code && code <= 57) || isMinusSign || isDecimalSign) {
- this.insert(event, char, { isDecimalSign, isMinusSign });
- }
- }
-
- onPaste(event: ClipboardEvent) {
- if (!this.disabled && !this.readonly) {
- event.preventDefault();
- let data = (event.clipboardData || (this.document as any).defaultView['clipboardData']).getData('Text');
- if (data) {
- if (this.maxlength) {
- data = data.toString().substring(0, this.maxlength);
- }
-
- let filteredData = this.parseValue(data);
- if (filteredData != null) {
- this.insert(event, filteredData.toString());
- }
- }
- }
- }
-
- allowMinusSign() {
- return this.min == null || this.min < 0;
- }
-
- isMinusSign(char: string) {
- if (this._minusSign.test(char) || char === '-') {
- this._minusSign.lastIndex = 0;
- return true;
- }
-
- return false;
- }
-
- isDecimalSign(char: string) {
- if (this._decimal.test(char)) {
- this._decimal.lastIndex = 0;
- return true;
- }
-
- return false;
- }
-
- isDecimalMode() {
- return this.mode === 'decimal';
- }
-
- getDecimalCharIndexes(val: string) {
- let decimalCharIndex = val.search(this._decimal);
- this._decimal.lastIndex = 0;
-
- const filteredVal = val
- .replace(this._prefix as RegExp, '')
- .trim()
- .replace(/\s/g, '')
- .replace(this._currency as RegExp, '');
- const decimalCharIndexWithoutPrefix = filteredVal.search(this._decimal);
- this._decimal.lastIndex = 0;
-
- return { decimalCharIndex, decimalCharIndexWithoutPrefix };
- }
-
- getCharIndexes(val: string) {
- const decimalCharIndex = val.search(this._decimal);
- this._decimal.lastIndex = 0;
- const minusCharIndex = val.search(this._minusSign);
- this._minusSign.lastIndex = 0;
- const suffixCharIndex = val.search(this._suffix as RegExp);
- (this._suffix as RegExp).lastIndex = 0;
- const currencyCharIndex = val.search(this._currency as RegExp);
- (this._currency as RegExp).lastIndex = 0;
-
- return { decimalCharIndex, minusCharIndex, suffixCharIndex, currencyCharIndex };
- }
-
- insert(event: Event, text: string, sign = { isDecimalSign: false, isMinusSign: false }) {
- const minusCharIndexOnText = text.search(this._minusSign);
- this._minusSign.lastIndex = 0;
- if (!this.allowMinusSign() && minusCharIndexOnText !== -1) {
- return;
- }
-
- let selectionStart = this.input?.nativeElement.selectionStart;
- let selectionEnd = this.input?.nativeElement.selectionEnd;
- let inputValue = this.input?.nativeElement.value.trim();
- const { decimalCharIndex, minusCharIndex, suffixCharIndex, currencyCharIndex } = this.getCharIndexes(inputValue);
- let newValueStr;
-
- if (sign.isMinusSign) {
- if (selectionStart === 0) {
- newValueStr = inputValue;
- if (minusCharIndex === -1 || selectionEnd !== 0) {
- newValueStr = this.insertText(inputValue, text, 0, selectionEnd);
- }
-
- this.updateValue(event, newValueStr, text, 'insert');
- }
- } else if (sign.isDecimalSign) {
- if (decimalCharIndex > 0 && selectionStart === decimalCharIndex) {
- this.updateValue(event, inputValue, text, 'insert');
- } else if (decimalCharIndex > selectionStart && decimalCharIndex < selectionEnd) {
- newValueStr = this.insertText(inputValue, text, selectionStart, selectionEnd);
- this.updateValue(event, newValueStr, text, 'insert');
- } else if (decimalCharIndex === -1 && this.maxFractionDigits) {
- newValueStr = this.insertText(inputValue, text, selectionStart, selectionEnd);
- this.updateValue(event, newValueStr, text, 'insert');
- }
- } else {
- const maxFractionDigits = this.numberFormat.resolvedOptions().maximumFractionDigits;
- const operation = selectionStart !== selectionEnd ? 'range-insert' : 'insert';
-
- if (decimalCharIndex > 0 && selectionStart > decimalCharIndex) {
- if (selectionStart + text.length - (decimalCharIndex + 1) <= maxFractionDigits) {
- const charIndex = currencyCharIndex >= selectionStart ? currencyCharIndex - 1 : suffixCharIndex >= selectionStart ? suffixCharIndex : inputValue.length;
-
- newValueStr = inputValue.slice(0, selectionStart) + text + inputValue.slice(selectionStart + text.length, charIndex) + inputValue.slice(charIndex);
- this.updateValue(event, newValueStr, text, operation);
- }
- } else {
- newValueStr = this.insertText(inputValue, text, selectionStart, selectionEnd);
- this.updateValue(event, newValueStr, text, operation);
- }
- }
- }
-
- insertText(value: string, text: string, start: number, end: number) {
- let textSplit = text === '.' ? text : text.split('.');
-
- if (textSplit.length === 2) {
- const decimalCharIndex = value.slice(start, end).search(this._decimal);
- this._decimal.lastIndex = 0;
- return decimalCharIndex > 0 ? value.slice(0, start) + this.formatValue(text) + value.slice(end) : value || this.formatValue(text);
- } else if (end - start === value.length) {
- return this.formatValue(text);
- } else if (start === 0) {
- return text + value.slice(end);
- } else if (end === value.length) {
- return value.slice(0, start) + text;
- } else {
- return value.slice(0, start) + text + value.slice(end);
- }
- }
-
- deleteRange(value: string, start: number, end: number) {
- let newValueStr;
-
- if (end - start === value.length) newValueStr = '';
- else if (start === 0) newValueStr = value.slice(end);
- else if (end === value.length) newValueStr = value.slice(0, start);
- else newValueStr = value.slice(0, start) + value.slice(end);
-
- return newValueStr;
- }
-
- initCursor() {
- let selectionStart = this.input?.nativeElement.selectionStart;
- let selectionEnd = this.input?.nativeElement.selectionEnd;
- let inputValue = this.input?.nativeElement.value;
- let valueLength = inputValue.length;
- let index = null;
-
- // remove prefix
- let prefixLength = (this.prefixChar || '').length;
- inputValue = inputValue.replace(this._prefix, '');
-
- // Will allow selecting whole prefix. But not a part of it.
- // Negative values will trigger clauses after this to fix the cursor position.
- if (selectionStart === selectionEnd || selectionStart !== 0 || selectionEnd < prefixLength) {
- selectionStart -= prefixLength;
- }
-
- let char = inputValue.charAt(selectionStart);
- if (this.isNumeralChar(char)) {
- return selectionStart + prefixLength;
- }
-
- //left
- let i = selectionStart - 1;
- while (i >= 0) {
- char = inputValue.charAt(i);
- if (this.isNumeralChar(char)) {
- index = i + prefixLength;
- break;
- } else {
- i--;
- }
- }
-
- if (index !== null) {
- this.input?.nativeElement.setSelectionRange(index + 1, index + 1);
- } else {
- i = selectionStart;
- while (i < valueLength) {
- char = inputValue.charAt(i);
- if (this.isNumeralChar(char)) {
- index = i + prefixLength;
- break;
- } else {
- i++;
- }
- }
-
- if (index !== null) {
- this.input?.nativeElement.setSelectionRange(index, index);
- }
- }
-
- return index || 0;
- }
-
- onInputClick() {
- const currentValue = this.input?.nativeElement.value;
-
- if (!this.readonly && currentValue !== getSelection()) {
- this.initCursor();
- }
- }
-
- isNumeralChar(char: string) {
- if (char.length === 1 && (this._numeral.test(char) || this._decimal.test(char) || this._group.test(char) || this._minusSign.test(char))) {
- this.resetRegex();
- return true;
- }
-
- return false;
- }
-
- resetRegex() {
- this._numeral.lastIndex = 0;
- this._decimal.lastIndex = 0;
- this._group.lastIndex = 0;
- this._minusSign.lastIndex = 0;
- }
-
- updateValue(event: Event, valueStr: Nullable, insertedValueStr: Nullable, operation: Nullable) {
- let currentValue = this.input?.nativeElement.value;
- let newValue = null;
-
- if (valueStr != null) {
- newValue = this.parseValue(valueStr);
- newValue = !newValue && !this.allowEmpty ? 0 : newValue;
- this.updateInput(newValue, insertedValueStr, operation, valueStr);
-
- this.handleOnInput(event, currentValue, newValue);
- }
- }
-
- handleOnInput(event: Event, currentValue: string, newValue: any) {
- if (this.isValueChanged(currentValue, newValue)) {
- (this.input as ElementRef).nativeElement.value = this.formatValue(newValue);
- this.input?.nativeElement.setAttribute('aria-valuenow', newValue);
- this.updateModel(event, newValue);
- this.onInput.emit({ originalEvent: event, value: newValue, formattedValue: currentValue });
- }
- }
-
- isValueChanged(currentValue: string, newValue: string) {
- if (newValue === null && currentValue !== null) {
- return true;
- }
-
- if (newValue != null) {
- let parsedCurrentValue = typeof currentValue === 'string' ? this.parseValue(currentValue) : currentValue;
- return newValue !== parsedCurrentValue;
- }
-
- return false;
- }
-
- validateValue(value: number | string) {
- if (value === '-' || value == null) {
- return null;
- }
-
- if (this.min != null && (value as number) < this.min) {
- return this.min;
- }
-
- if (this.max != null && (value as number) > this.max) {
- return this.max;
- }
-
- return value;
- }
-
- updateInput(value: any, insertedValueStr: Nullable, operation: Nullable, valueStr: Nullable) {
- insertedValueStr = insertedValueStr || '';
-
- let inputValue = this.input?.nativeElement.value;
- let newValue = this.formatValue(value);
- let currentLength = inputValue.length;
-
- if (newValue !== valueStr) {
- newValue = this.concatValues(newValue, valueStr as string);
- }
-
- if (currentLength === 0) {
- this.input.nativeElement.value = newValue;
- this.input.nativeElement.setSelectionRange(0, 0);
- const index = this.initCursor();
- const selectionEnd = index + insertedValueStr.length;
- this.input.nativeElement.setSelectionRange(selectionEnd, selectionEnd);
- } else {
- let selectionStart = this.input.nativeElement.selectionStart;
- let selectionEnd = this.input.nativeElement.selectionEnd;
-
- if (this.maxlength && newValue.length > this.maxlength) {
- newValue = newValue.slice(0, this.maxlength);
- selectionStart = Math.min(selectionStart, this.maxlength);
- selectionEnd = Math.min(selectionEnd, this.maxlength);
- }
-
- if (this.maxlength && this.maxlength < newValue.length) {
- return;
- }
-
- this.input.nativeElement.value = newValue;
- let newLength = newValue.length;
-
- if (operation === 'range-insert') {
- const startValue = this.parseValue((inputValue || '').slice(0, selectionStart));
- const startValueStr = startValue !== null ? startValue.toString() : '';
- const startExpr = startValueStr.split('').join(`(${this.groupChar})?`);
- const sRegex = new RegExp(startExpr, 'g');
- sRegex.test(newValue);
-
- const tExpr = insertedValueStr.split('').join(`(${this.groupChar})?`);
- const tRegex = new RegExp(tExpr, 'g');
- tRegex.test(newValue.slice(sRegex.lastIndex));
-
- selectionEnd = sRegex.lastIndex + tRegex.lastIndex;
- this.input.nativeElement.setSelectionRange(selectionEnd, selectionEnd);
- } else if (newLength === currentLength) {
- if (operation === 'insert' || operation === 'delete-back-single') this.input.nativeElement.setSelectionRange(selectionEnd + 1, selectionEnd + 1);
- else if (operation === 'delete-single') this.input.nativeElement.setSelectionRange(selectionEnd - 1, selectionEnd - 1);
- else if (operation === 'delete-range' || operation === 'spin') this.input.nativeElement.setSelectionRange(selectionEnd, selectionEnd);
- } else if (operation === 'delete-back-single') {
- let prevChar = inputValue.charAt(selectionEnd - 1);
- let nextChar = inputValue.charAt(selectionEnd);
- let diff = currentLength - newLength;
- let isGroupChar = this._group.test(nextChar);
-
- if (isGroupChar && diff === 1) {
- selectionEnd += 1;
- } else if (!isGroupChar && this.isNumeralChar(prevChar)) {
- selectionEnd += -1 * diff + 1;
- }
-
- this._group.lastIndex = 0;
- this.input.nativeElement.setSelectionRange(selectionEnd, selectionEnd);
- } else if (inputValue === '-' && operation === 'insert') {
- this.input.nativeElement.setSelectionRange(0, 0);
- const index = this.initCursor();
- const selectionEnd = index + insertedValueStr.length + 1;
- this.input.nativeElement.setSelectionRange(selectionEnd, selectionEnd);
- } else {
- selectionEnd = selectionEnd + (newLength - currentLength);
- this.input.nativeElement.setSelectionRange(selectionEnd, selectionEnd);
- }
- }
-
- this.input.nativeElement.setAttribute('aria-valuenow', value);
- }
-
- concatValues(val1: string, val2: string) {
- if (val1 && val2) {
- let decimalCharIndex = val2.search(this._decimal);
- this._decimal.lastIndex = 0;
-
- if (this.suffixChar) {
- return decimalCharIndex !== -1 ? val1.replace(this.suffixChar, '').split(this._decimal)[0] + val2.replace(this.suffixChar, '').slice(decimalCharIndex) + this.suffixChar : val1;
- } else {
- return decimalCharIndex !== -1 ? val1.split(this._decimal)[0] + val2.slice(decimalCharIndex) : val1;
- }
- }
- return val1;
- }
-
- getDecimalLength(value: string) {
- if (value) {
- const valueSplit = value.split(this._decimal);
-
- if (valueSplit.length === 2) {
- return valueSplit[1]
- .replace(this._suffix as RegExp, '')
- .trim()
- .replace(/\s/g, '')
- .replace(this._currency as RegExp, '').length;
- }
- }
-
- return 0;
- }
-
- onInputFocus(event: Event) {
- this.focused = true;
- this.onFocus.emit(event);
- }
-
- onInputBlur(event: Event) {
- this.focused = false;
-
- const newValueNumber = this.validateValue(this.parseValue(this.input.nativeElement.value));
- const newValueString = newValueNumber?.toString();
- this.input.nativeElement.value = this.formatValue(newValueString);
- this.input.nativeElement.setAttribute('aria-valuenow', newValueString);
- this.updateModel(event, newValueNumber);
- this.onBlur.emit(event);
- }
-
- formattedValue() {
- const val = !this.value && !this.allowEmpty ? 0 : this.value;
- return this.formatValue(val);
- }
-
- updateModel(event: Event, value: any) {
- const isBlurUpdateOnMode = this.ngControl?.control?.updateOn === 'blur';
-
- if (this.value !== value) {
- this.value = value;
-
- if (!(isBlurUpdateOnMode && this.focused)) {
- this.onModelChange(value);
- }
- } else if (isBlurUpdateOnMode) {
- this.onModelChange(value);
- }
- this.onModelTouched();
- }
-
- writeValue(value: any): void {
- this.value = value ? Number(value) : value;
- this.cd.markForCheck();
- }
-
- registerOnChange(fn: Function): void {
- this.onModelChange = fn;
- }
-
- registerOnTouched(fn: Function): void {
- this.onModelTouched = fn;
- }
-
- setDisabledState(val: boolean): void {
- this.disabled = val;
- this.cd.markForCheck();
- }
-
- get filled() {
- return this.value != null && this.value.toString().length > 0;
- }
-
- clearTimer() {
- if (this.timer) {
- clearInterval(this.timer);
- }
- }
-}
-
-@NgModule({
- imports: [InputNumber, SharedModule],
- exports: [InputNumber, SharedModule]
-})
+import { CommonModule } from '@angular/common';
+import {
+ AfterContentInit,
+ booleanAttribute,
+ ChangeDetectionStrategy,
+ Component,
+ ContentChild,
+ ContentChildren,
+ ElementRef,
+ EventEmitter,
+ forwardRef,
+ HostBinding,
+ inject,
+ Injector,
+ Input,
+ NgModule,
+ numberAttribute,
+ OnChanges,
+ OnInit,
+ Output,
+ QueryList,
+ SimpleChanges,
+ TemplateRef,
+ ViewChild,
+ ViewEncapsulation
+} from '@angular/core';
+import { ControlValueAccessor, NG_VALUE_ACCESSOR, NgControl } from '@angular/forms';
+import { getSelection } from '@primeuix/utils';
+import { PrimeTemplate, SharedModule } from 'primeng/api';
+import { AutoFocus } from 'primeng/autofocus';
+import { BaseComponent } from 'primeng/basecomponent';
+import { AngleDownIcon, AngleUpIcon, TimesIcon } from 'primeng/icons';
+import { InputText } from 'primeng/inputtext';
+import { Nullable } from 'primeng/ts-helpers';
+import { InputNumberInputEvent } from './inputnumber.interface';
+import { InputNumberStyle } from './style/inputnumberstyle';
+
+export const INPUTNUMBER_VALUE_ACCESSOR: any = {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => InputNumber),
+ multi: true
+};
+/**
+ * InputNumber is an input component to provide numerical input.
+ * @group Components
+ */
+@Component({
+ selector: 'p-inputNumber, p-inputnumber, p-input-number',
+ standalone: true,
+ imports: [CommonModule, InputText, AutoFocus, TimesIcon, AngleUpIcon, AngleDownIcon, SharedModule],
+ template: `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ providers: [INPUTNUMBER_VALUE_ACCESSOR, InputNumberStyle],
+ encapsulation: ViewEncapsulation.None,
+ host: {
+ '[attr.data-pc-name]': "'inputnumber'",
+ '[attr.data-pc-section]': "'root'",
+ '[class]': 'hostClass'
+ }
+})
+export class InputNumber extends BaseComponent implements OnInit, AfterContentInit, OnChanges, ControlValueAccessor {
+ /**
+ * Displays spinner buttons.
+ * @group Props
+ */
+ @Input({ transform: booleanAttribute }) showButtons: boolean = false;
+ /**
+ * Whether to format the value.
+ * @group Props
+ */
+ @Input({ transform: booleanAttribute }) format: boolean = true;
+ /**
+ * Layout of the buttons, valid values are "stacked" (default), "horizontal" and "vertical".
+ * @group Props
+ */
+ @Input() buttonLayout: string = 'stacked';
+ /**
+ * Identifier of the focus input to match a label defined for the component.
+ * @group Props
+ */
+ @Input() inputId: string | undefined;
+ /**
+ * Style class of the component.
+ * @group Props
+ */
+ @Input() styleClass: string | undefined;
+ /**
+ * Inline style of the component.
+ * @group Props
+ */
+ @Input() style: { [klass: string]: any } | null | undefined;
+ /**
+ * Advisory information to display on input.
+ * @group Props
+ */
+ @Input() placeholder: string | undefined;
+ /**
+ * Defines the size of the component.
+ * @group Props
+ */
+ @Input() size: 'large' | 'small';
+ /**
+ * Maximum number of character allows in the input field.
+ * @group Props
+ */
+ @Input({ transform: numberAttribute }) maxlength: number | undefined;
+ /**
+ * Specifies tab order of the element.
+ * @group Props
+ */
+ @Input({ transform: numberAttribute }) tabindex: number | undefined;
+ /**
+ * Title text of the input text.
+ * @group Props
+ */
+ @Input() title: string | undefined;
+ /**
+ * Specifies one or more IDs in the DOM that labels the input field.
+ * @group Props
+ */
+ @Input() ariaLabelledBy: string | undefined;
+ /**
+ * Specifies one or more IDs in the DOM that describes the input field.
+ * @group Props
+ */
+ @Input() ariaDescribedBy: string | undefined;
+ /**
+ * Used to define a string that labels the input element.
+ * @group Props
+ */
+ @Input() ariaLabel: string | undefined;
+ /**
+ * Used to indicate that user input is required on an element before a form can be submitted.
+ * @group Props
+ */
+ @Input({ transform: booleanAttribute }) ariaRequired: boolean | undefined;
+ /**
+ * Name of the input field.
+ * @group Props
+ */
+ @Input() name: string | undefined;
+ /**
+ * Indicates that whether the input field is required.
+ * @group Props
+ */
+ @Input({ transform: booleanAttribute }) required: boolean | undefined;
+ /**
+ * Used to define a string that autocomplete attribute the current element.
+ * @group Props
+ */
+ @Input() autocomplete: string | undefined;
+ /**
+ * Mininum boundary value.
+ * @group Props
+ */
+ @Input({ transform: numberAttribute }) min: number | undefined;
+ /**
+ * Maximum boundary value.
+ * @group Props
+ */
+ @Input({ transform: numberAttribute }) max: number | undefined;
+ /**
+ * Style class of the increment button.
+ * @group Props
+ */
+ @Input() incrementButtonClass: string | undefined;
+ /**
+ * Style class of the decrement button.
+ * @group Props
+ */
+ @Input() decrementButtonClass: string | undefined;
+ /**
+ * Style class of the increment button.
+ * @group Props
+ */
+ @Input() incrementButtonIcon: string | undefined;
+ /**
+ * Style class of the decrement button.
+ * @group Props
+ */
+ @Input() decrementButtonIcon: string | undefined;
+ /**
+ * When present, it specifies that an input field is read-only.
+ * @group Props
+ */
+ @Input({ transform: booleanAttribute }) readonly: boolean = false;
+ /**
+ * Step factor to increment/decrement the value.
+ * @group Props
+ */
+ @Input({ transform: numberAttribute }) step: number = 1;
+ /**
+ * Determines whether the input field is empty.
+ * @group Props
+ */
+ @Input({ transform: booleanAttribute }) allowEmpty: boolean = true;
+ /**
+ * Locale to be used in formatting.
+ * @group Props
+ */
+ @Input() locale: string | undefined;
+ /**
+ * The locale matching algorithm to use. Possible values are "lookup" and "best fit"; the default is "best fit". See Locale Negotiation for details.
+ * @group Props
+ */
+ @Input() localeMatcher: any;
+ /**
+ * Defines the behavior of the component, valid values are "decimal" and "currency".
+ * @group Props
+ */
+ @Input() mode: string | any = 'decimal';
+ /**
+ * The currency to use in currency formatting. Possible values are the ISO 4217 currency codes, such as "USD" for the US dollar, "EUR" for the euro, or "CNY" for the Chinese RMB. There is no default value; if the style is "currency", the currency property must be provided.
+ * @group Props
+ */
+ @Input() currency: string | undefined;
+ /**
+ * How to display the currency in currency formatting. Possible values are "symbol" to use a localized currency symbol such as €, ü"code" to use the ISO currency code, "name" to use a localized currency name such as "dollar"; the default is "symbol".
+ * @group Props
+ */
+ @Input() currencyDisplay: string | undefined | any;
+ /**
+ * Whether to use grouping separators, such as thousands separators or thousand/lakh/crore separators.
+ * @group Props
+ */
+ @Input({ transform: booleanAttribute }) useGrouping: boolean = true;
+ /**
+ * Specifies the input variant of the component.
+ * @group Props
+ */
+ @Input() variant: 'filled' | 'outlined';
+ /**
+ * The minimum number of fraction digits to use. Possible values are from 0 to 20; the default for plain number and percent formatting is 0; the default for currency formatting is the number of minor unit digits provided by the ISO 4217 currency code list (2 if the list doesn't provide that information).
+ * @group Props
+ */
+ @Input({ transform: (value: unknown) => numberAttribute(value, null) }) minFractionDigits: number | undefined;
+ /**
+ * The maximum number of fraction digits to use. Possible values are from 0 to 20; the default for plain number formatting is the larger of minimumFractionDigits and 3; the default for currency formatting is the larger of minimumFractionDigits and the number of minor unit digits provided by the ISO 4217 currency code list (2 if the list doesn't provide that information).
+ * @group Props
+ */
+ @Input({ transform: (value: unknown) => numberAttribute(value, null) }) maxFractionDigits: number | undefined;
+ /**
+ * Text to display before the value.
+ * @group Props
+ */
+ @Input() prefix: string | undefined;
+ /**
+ * Text to display after the value.
+ * @group Props
+ */
+ @Input() suffix: string | undefined;
+ /**
+ * Inline style of the input field.
+ * @group Props
+ */
+ @Input() inputStyle: any;
+ /**
+ * Style class of the input field.
+ * @group Props
+ */
+ @Input() inputStyleClass: string | undefined;
+ /**
+ * When enabled, a clear icon is displayed to clear the value.
+ * @group Props
+ */
+ @Input({ transform: booleanAttribute }) showClear: boolean = false;
+ /**
+ * When present, it specifies that the component should automatically get focus on load.
+ * @group Props
+ */
+ @Input({ transform: booleanAttribute }) autofocus: boolean | undefined;
+ /**
+ * When present, it specifies that the element should be disabled.
+ * @group Props
+ */
+ @Input() get disabled(): boolean | undefined {
+ return this._disabled;
+ }
+ set disabled(disabled: boolean | undefined) {
+ if (disabled) this.focused = false;
+
+ this._disabled = disabled;
+
+ if (this.timer) this.clearTimer();
+ }
+ /**
+ * Spans 100% width of the container when enabled.
+ * @group Props
+ */
+ @Input({ transform: booleanAttribute }) fluid: boolean = false;
+ /**
+ * Callback to invoke on input.
+ * @param {InputNumberInputEvent} event - Custom input event.
+ * @group Emits
+ */
+ @Output() onInput: EventEmitter = new EventEmitter();
+ /**
+ * Callback to invoke when the component receives focus.
+ * @param {Event} event - Browser event.
+ * @group Emits
+ */
+ @Output() onFocus: EventEmitter = new EventEmitter();
+ /**
+ * Callback to invoke when the component loses focus.
+ * @param {Event} event - Browser event.
+ * @group Emits
+ */
+ @Output() onBlur: EventEmitter = new EventEmitter();
+ /**
+ * Callback to invoke on input key press.
+ * @param {KeyboardEvent} event - Keyboard event.
+ * @group Emits
+ */
+ @Output() onKeyDown: EventEmitter = new EventEmitter();
+ /**
+ * Callback to invoke when clear token is clicked.
+ * @group Emits
+ */
+ @Output() onClear: EventEmitter = new EventEmitter();
+
+ /**
+ * Template of the clear icon.
+ * @group Templates
+ */
+ @ContentChild('clearicon', { descendants: false }) clearIconTemplate: Nullable>;
+ /**
+ * Template of the increment button icon.
+ * @group Templates
+ */
+ @ContentChild('incrementbuttonicon', { descendants: false }) incrementButtonIconTemplate: Nullable>;
+
+ /**
+ * Template of the decrement button icon.
+ * @group Templates
+ */
+ @ContentChild('decrementbuttonicon', { descendants: false }) decrementButtonIconTemplate: Nullable>;
+
+ @ContentChildren(PrimeTemplate) templates!: QueryList;
+
+ @ViewChild('input') input!: ElementRef;
+
+ _clearIconTemplate: TemplateRef | undefined;
+
+ _incrementButtonIconTemplate: TemplateRef | undefined;
+
+ _decrementButtonIconTemplate: TemplateRef | undefined;
+
+ value: Nullable;
+
+ onModelChange: Function = () => {};
+
+ onModelTouched: Function = () => {};
+
+ focused: Nullable;
+
+ initialized: Nullable;
+
+ groupChar: string = '';
+
+ prefixChar: string = '';
+
+ suffixChar: string = '';
+
+ isSpecialChar: Nullable;
+
+ timer: any;
+
+ lastValue: Nullable;
+
+ _numeral: any;
+
+ numberFormat: any;
+
+ _decimal: any;
+
+ _decimalChar: string;
+
+ _group: any;
+
+ _minusSign: any;
+
+ _currency: Nullable;
+
+ _prefix: Nullable;
+
+ _suffix: Nullable;
+
+ _index: number | any;
+
+ _disabled: boolean | undefined;
+
+ _componentStyle = inject(InputNumberStyle);
+
+ private ngControl: NgControl | null = null;
+
+ get _rootClass() {
+ return this._componentStyle.classes.root({ instance: this });
+ }
+
+ get hasFluid() {
+ const nativeElement = this.el.nativeElement;
+ const fluidComponent = nativeElement.closest('p-fluid');
+ return this.fluid || !!fluidComponent;
+ }
+
+ get _incrementButtonClass() {
+ return this._componentStyle.classes.incrementButton({ instance: this });
+ }
+
+ get _decrementButtonClass() {
+ return this._componentStyle.classes.decrementButton({ instance: this });
+ }
+
+ constructor(public readonly injector: Injector) {
+ super();
+ }
+
+ ngOnChanges(simpleChange: SimpleChanges) {
+ super.ngOnChanges(simpleChange);
+ const props = ['locale', 'localeMatcher', 'mode', 'currency', 'currencyDisplay', 'useGrouping', 'minFractionDigits', 'maxFractionDigits', 'prefix', 'suffix'];
+ if (props.some((p) => !!simpleChange[p])) {
+ this.updateConstructParser();
+ }
+ }
+
+ get hostClass(): string {
+ return [
+ 'p-inputnumber p-component p-inputwrapper',
+ this.styleClass,
+ this.filled || this.allowEmpty === false ? 'p-inputwrapper-filled' : '',
+ this.focused ? 'p-inputwrapper-focus' : '',
+ this.showButtons && this.buttonLayout === 'stacked' ? 'p-inputnumber-stacked' : '',
+ this.showButtons && this.buttonLayout === 'horizontal' ? 'p-inputnumber-horizontal' : '',
+ this.showButtons && this.buttonLayout === 'vertical' ? 'p-inputnumber-vertical' : '',
+ this.hasFluid ? 'p-inputnumber-fluid' : ''
+ ]
+ .filter((cls) => !!cls)
+ .join(' ');
+ }
+
+ @HostBinding('style') get hostStyle(): any {
+ return this.style;
+ }
+
+ ngOnInit() {
+ super.ngOnInit();
+
+ this.ngControl = this.injector.get(NgControl, null, { optional: true });
+
+ this.constructParser();
+
+ this.initialized = true;
+ }
+
+ ngAfterContentInit() {
+ this.templates.forEach((item) => {
+ switch (item.getType()) {
+ case 'clearicon':
+ this._clearIconTemplate = item.template;
+ break;
+
+ case 'incrementbuttonicon':
+ this._incrementButtonIconTemplate = item.template;
+ break;
+
+ case 'decrementbuttonicon':
+ this._decrementButtonIconTemplate = item.template;
+ break;
+ }
+ });
+ }
+
+ getOptions() {
+ return {
+ localeMatcher: this.localeMatcher,
+ style: this.mode,
+ currency: this.currency,
+ currencyDisplay: this.currencyDisplay,
+ useGrouping: this.useGrouping,
+ minimumFractionDigits: this.minFractionDigits ?? undefined,
+ maximumFractionDigits: this.maxFractionDigits ?? undefined
+ };
+ }
+
+ constructParser() {
+ this.numberFormat = new Intl.NumberFormat(this.locale, this.getOptions());
+ const numerals = [...new Intl.NumberFormat(this.locale, { useGrouping: false }).format(9876543210)].reverse();
+ const index = new Map(numerals.map((d, i) => [d, i]));
+ this._numeral = new RegExp(`[${numerals.join('')}]`, 'g');
+ this._group = this.getGroupingExpression();
+ this._minusSign = this.getMinusSignExpression();
+ this._currency = this.getCurrencyExpression();
+ this._decimal = this.getDecimalExpression();
+ this._decimalChar = this.getDecimalChar();
+ this._suffix = this.getSuffixExpression();
+ this._prefix = this.getPrefixExpression();
+ this._index = (d: any) => index.get(d);
+ }
+
+ updateConstructParser() {
+ if (this.initialized) {
+ this.constructParser();
+ }
+ }
+
+ escapeRegExp(text: string): string {
+ return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
+ }
+
+ getDecimalExpression(): RegExp {
+ const decimalChar = this.getDecimalChar();
+ return new RegExp(`[${decimalChar}]`, 'g');
+ }
+ getDecimalChar(): string {
+ const formatter = new Intl.NumberFormat(this.locale, { ...this.getOptions(), useGrouping: false });
+ return formatter
+ .format(1.1)
+ .replace(this._currency as RegExp | string, '')
+ .trim()
+ .replace(this._numeral, '');
+ }
+
+ getGroupingExpression(): RegExp {
+ const formatter = new Intl.NumberFormat(this.locale, { useGrouping: true });
+ this.groupChar = formatter.format(1000000).trim().replace(this._numeral, '').charAt(0);
+ return new RegExp(`[${this.groupChar}]`, 'g');
+ }
+
+ getMinusSignExpression(): RegExp {
+ const formatter = new Intl.NumberFormat(this.locale, { useGrouping: false });
+ return new RegExp(`[${formatter.format(-1).trim().replace(this._numeral, '')}]`, 'g');
+ }
+
+ getCurrencyExpression(): RegExp {
+ if (this.currency) {
+ const formatter = new Intl.NumberFormat(this.locale, {
+ style: 'currency',
+ currency: this.currency,
+ currencyDisplay: this.currencyDisplay,
+ minimumFractionDigits: 0,
+ maximumFractionDigits: 0
+ });
+ return new RegExp(`[${formatter.format(1).replace(/\s/g, '').replace(this._numeral, '').replace(this._group, '')}]`, 'g');
+ }
+
+ return new RegExp(`[]`, 'g');
+ }
+
+ getPrefixExpression(): RegExp {
+ if (this.prefix) {
+ this.prefixChar = this.prefix;
+ } else {
+ const formatter = new Intl.NumberFormat(this.locale, {
+ style: this.mode,
+ currency: this.currency,
+ currencyDisplay: this.currencyDisplay
+ });
+ this.prefixChar = formatter.format(1).split('1')[0];
+ }
+
+ return new RegExp(`${this.escapeRegExp(this.prefixChar || '')}`, 'g');
+ }
+
+ getSuffixExpression(): RegExp {
+ if (this.suffix) {
+ this.suffixChar = this.suffix;
+ } else {
+ const formatter = new Intl.NumberFormat(this.locale, {
+ style: this.mode,
+ currency: this.currency,
+ currencyDisplay: this.currencyDisplay,
+ minimumFractionDigits: 0,
+ maximumFractionDigits: 0
+ });
+ this.suffixChar = formatter.format(1).split('1')[1];
+ }
+
+ return new RegExp(`${this.escapeRegExp(this.suffixChar || '')}`, 'g');
+ }
+
+ formatValue(value: any) {
+ if (value != null) {
+ if (value === '-') {
+ // Minus sign
+ return value;
+ }
+
+ if (this.format) {
+ let formatter = new Intl.NumberFormat(this.locale, this.getOptions());
+ let formattedValue = formatter.format(value);
+
+ if (this.prefix && value != this.prefix) {
+ formattedValue = this.prefix + formattedValue;
+ }
+
+ if (this.suffix && value != this.suffix) {
+ formattedValue = formattedValue + this.suffix;
+ }
+
+ return formattedValue;
+ }
+
+ return value.toString();
+ }
+
+ return '';
+ }
+
+ parseValue(text: any) {
+ const suffixRegex = new RegExp(this._suffix, '');
+ const prefixRegex = new RegExp(this._prefix, '');
+ const currencyRegex = new RegExp(this._currency, '');
+
+ let filteredText = text
+ .replace(suffixRegex, '')
+ .replace(prefixRegex, '')
+ .trim()
+ .replace(/\s/g, '')
+ .replace(currencyRegex, '')
+ .replace(this._group, '')
+ .replace(this._minusSign, '-')
+ .replace(this._decimal, '.')
+ .replace(this._numeral, this._index);
+
+ if (filteredText) {
+ if (filteredText === '-')
+ // Minus sign
+ return filteredText;
+
+ let parsedValue = +filteredText;
+ return isNaN(parsedValue) ? null : parsedValue;
+ }
+
+ return null;
+ }
+
+ repeat(event: Event, interval: number | null, dir: number) {
+ if (this.readonly) {
+ return;
+ }
+
+ let i = interval || 500;
+
+ this.clearTimer();
+ this.timer = setTimeout(() => {
+ this.repeat(event, 40, dir);
+ }, i);
+
+ this.spin(event, dir);
+ }
+
+ spin(event: Event, dir: number) {
+ let step = this.step * dir;
+ let currentValue = this.parseValue(this.input?.nativeElement.value) || 0;
+ let newValue = this.validateValue((currentValue as number) + step);
+ if (this.maxlength && this.maxlength < this.formatValue(newValue).length) {
+ return;
+ }
+ this.updateInput(newValue, null, 'spin', null);
+ this.updateModel(event, newValue);
+
+ this.handleOnInput(event, currentValue, newValue);
+ }
+
+ clear() {
+ this.value = null;
+ this.onModelChange(this.value);
+ this.onClear.emit();
+ }
+
+ onUpButtonMouseDown(event: MouseEvent) {
+ if (event.button === 2) {
+ this.clearTimer();
+ return;
+ }
+
+ if (!this.disabled) {
+ this.input?.nativeElement.focus();
+ this.repeat(event, null, 1);
+ event.preventDefault();
+ }
+ }
+
+ onUpButtonMouseUp() {
+ if (!this.disabled) {
+ this.clearTimer();
+ }
+ }
+
+ onUpButtonMouseLeave() {
+ if (!this.disabled) {
+ this.clearTimer();
+ }
+ }
+
+ onUpButtonKeyDown(event: KeyboardEvent) {
+ if (event.keyCode === 32 || event.keyCode === 13) {
+ this.repeat(event, null, 1);
+ }
+ }
+
+ onUpButtonKeyUp() {
+ if (!this.disabled) {
+ this.clearTimer();
+ }
+ }
+
+ onDownButtonMouseDown(event: MouseEvent) {
+ if (event.button === 2) {
+ this.clearTimer();
+ return;
+ }
+ if (!this.disabled) {
+ this.input?.nativeElement.focus();
+ this.repeat(event, null, -1);
+ event.preventDefault();
+ }
+ }
+
+ onDownButtonMouseUp() {
+ if (!this.disabled) {
+ this.clearTimer();
+ }
+ }
+
+ onDownButtonMouseLeave() {
+ if (!this.disabled) {
+ this.clearTimer();
+ }
+ }
+
+ onDownButtonKeyUp() {
+ if (!this.disabled) {
+ this.clearTimer();
+ }
+ }
+
+ onDownButtonKeyDown(event: KeyboardEvent) {
+ if (event.keyCode === 32 || event.keyCode === 13) {
+ this.repeat(event, null, -1);
+ }
+ }
+
+ onUserInput(event: Event) {
+ if (this.readonly) {
+ return;
+ }
+
+ if (this.isSpecialChar) {
+ (event.target as HTMLInputElement).value = this.lastValue as string;
+ }
+ this.isSpecialChar = false;
+ }
+
+ onInputKeyDown(event: KeyboardEvent) {
+ if (this.readonly) {
+ return;
+ }
+
+ this.lastValue = (event.target as HTMLInputElement).value;
+ if ((event as KeyboardEvent).shiftKey || (event as KeyboardEvent).altKey) {
+ this.isSpecialChar = true;
+ return;
+ }
+
+ let selectionStart = (event.target as HTMLInputElement).selectionStart as number;
+ let selectionEnd = (event.target as HTMLInputElement).selectionEnd as number;
+ let inputValue = (event.target as HTMLInputElement).value as string;
+ let newValueStr = null;
+
+ if (event.altKey) {
+ event.preventDefault();
+ }
+
+ switch (event.key) {
+ case 'ArrowUp':
+ this.spin(event, 1);
+ event.preventDefault();
+ break;
+
+ case 'ArrowDown':
+ this.spin(event, -1);
+ event.preventDefault();
+ break;
+
+ case 'ArrowLeft':
+ for (let index = selectionStart; index <= inputValue.length; index++) {
+ const previousCharIndex = index === 0 ? 0 : index - 1;
+ if (this.isNumeralChar(inputValue.charAt(previousCharIndex))) {
+ this.input.nativeElement.setSelectionRange(index, index);
+ break;
+ }
+ }
+ break;
+
+ case 'ArrowRight':
+ for (let index = selectionEnd; index >= 0; index--) {
+ if (this.isNumeralChar(inputValue.charAt(index))) {
+ this.input.nativeElement.setSelectionRange(index, index);
+ break;
+ }
+ }
+ break;
+
+ case 'Tab':
+ case 'Enter':
+ newValueStr = this.validateValue(this.parseValue(this.input.nativeElement.value));
+ this.input.nativeElement.value = this.formatValue(newValueStr);
+ this.input.nativeElement.setAttribute('aria-valuenow', newValueStr);
+ this.updateModel(event, newValueStr);
+ break;
+
+ case 'Backspace': {
+ event.preventDefault();
+
+ if (selectionStart === selectionEnd) {
+ if ((selectionStart == 1 && this.prefix) || (selectionStart == inputValue.length && this.suffix)) {
+ break;
+ }
+
+ const deleteChar = inputValue.charAt(selectionStart - 1);
+ const { decimalCharIndex, decimalCharIndexWithoutPrefix } = this.getDecimalCharIndexes(inputValue);
+
+ if (this.isNumeralChar(deleteChar)) {
+ const decimalLength = this.getDecimalLength(inputValue);
+
+ if (this._group.test(deleteChar)) {
+ this._group.lastIndex = 0;
+ newValueStr = inputValue.slice(0, selectionStart - 2) + inputValue.slice(selectionStart - 1);
+ } else if (this._decimal.test(deleteChar)) {
+ this._decimal.lastIndex = 0;
+
+ if (decimalLength) {
+ this.input?.nativeElement.setSelectionRange(selectionStart - 1, selectionStart - 1);
+ } else {
+ newValueStr = inputValue.slice(0, selectionStart - 1) + inputValue.slice(selectionStart);
+ }
+ } else if (decimalCharIndex > 0 && selectionStart > decimalCharIndex) {
+ const insertedText = this.isDecimalMode() && (this.minFractionDigits || 0) < decimalLength ? '' : '0';
+ newValueStr = inputValue.slice(0, selectionStart - 1) + insertedText + inputValue.slice(selectionStart);
+ } else if (decimalCharIndexWithoutPrefix === 1) {
+ newValueStr = inputValue.slice(0, selectionStart - 1) + '0' + inputValue.slice(selectionStart);
+ newValueStr = (this.parseValue(newValueStr) as number) > 0 ? newValueStr : '';
+ } else {
+ newValueStr = inputValue.slice(0, selectionStart - 1) + inputValue.slice(selectionStart);
+ }
+ } else if (this.mode === 'currency' && deleteChar.search(this._currency) != -1) {
+ newValueStr = inputValue.slice(1);
+ }
+
+ this.updateValue(event, newValueStr, null, 'delete-single');
+ } else {
+ newValueStr = this.deleteRange(inputValue, selectionStart, selectionEnd);
+ this.updateValue(event, newValueStr, null, 'delete-range');
+ }
+
+ break;
+ }
+
+ case 'Delete':
+ event.preventDefault();
+
+ if (selectionStart === selectionEnd) {
+ if ((selectionStart == 0 && this.prefix) || (selectionStart == inputValue.length - 1 && this.suffix)) {
+ break;
+ }
+ const deleteChar = inputValue.charAt(selectionStart);
+ const { decimalCharIndex, decimalCharIndexWithoutPrefix } = this.getDecimalCharIndexes(inputValue);
+
+ if (this.isNumeralChar(deleteChar)) {
+ const decimalLength = this.getDecimalLength(inputValue);
+
+ if (this._group.test(deleteChar)) {
+ this._group.lastIndex = 0;
+ newValueStr = inputValue.slice(0, selectionStart) + inputValue.slice(selectionStart + 2);
+ } else if (this._decimal.test(deleteChar)) {
+ this._decimal.lastIndex = 0;
+
+ if (decimalLength) {
+ this.input?.nativeElement.setSelectionRange(selectionStart + 1, selectionStart + 1);
+ } else {
+ newValueStr = inputValue.slice(0, selectionStart) + inputValue.slice(selectionStart + 1);
+ }
+ } else if (decimalCharIndex > 0 && selectionStart > decimalCharIndex) {
+ const insertedText = this.isDecimalMode() && (this.minFractionDigits || 0) < decimalLength ? '' : '0';
+ newValueStr = inputValue.slice(0, selectionStart) + insertedText + inputValue.slice(selectionStart + 1);
+ } else if (decimalCharIndexWithoutPrefix === 1) {
+ newValueStr = inputValue.slice(0, selectionStart) + '0' + inputValue.slice(selectionStart + 1);
+ newValueStr = (this.parseValue(newValueStr) as number) > 0 ? newValueStr : '';
+ } else {
+ newValueStr = inputValue.slice(0, selectionStart) + inputValue.slice(selectionStart + 1);
+ }
+ }
+
+ this.updateValue(event, newValueStr as string, null, 'delete-back-single');
+ } else {
+ newValueStr = this.deleteRange(inputValue, selectionStart, selectionEnd);
+ this.updateValue(event, newValueStr, null, 'delete-range');
+ }
+ break;
+
+ case 'Home':
+ if (this.min) {
+ this.updateModel(event, this.min);
+ event.preventDefault();
+ }
+ break;
+
+ case 'End':
+ if (this.max) {
+ this.updateModel(event, this.max);
+ event.preventDefault();
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ this.onKeyDown.emit(event);
+ }
+
+ onInputKeyPress(event: KeyboardEvent) {
+ if (this.readonly) {
+ return;
+ }
+
+ let code = event.which || event.keyCode;
+ let char = String.fromCharCode(code);
+ let isDecimalSign = this.isDecimalSign(char);
+ const isMinusSign = this.isMinusSign(char);
+
+ if (code != 13) {
+ event.preventDefault();
+ }
+ if (!isDecimalSign && event.code === 'NumpadDecimal') {
+ isDecimalSign = true;
+ char = this._decimalChar;
+ code = char.charCodeAt(0);
+ }
+ const { value, selectionStart, selectionEnd } = this.input.nativeElement;
+ const newValue = this.parseValue(value + char);
+ const newValueStr = newValue != null ? newValue.toString() : '';
+ const selectedValue = value.substring(selectionStart, selectionEnd);
+ const selectedValueParsed = this.parseValue(selectedValue);
+ const selectedValueStr = selectedValueParsed != null ? selectedValueParsed.toString() : '';
+
+ if (selectionStart !== selectionEnd && selectedValueStr.length > 0) {
+ this.insert(event, char, { isDecimalSign, isMinusSign });
+ return;
+ }
+
+ if (this.maxlength && newValueStr.length > this.maxlength) {
+ return;
+ }
+
+ if ((48 <= code && code <= 57) || isMinusSign || isDecimalSign) {
+ this.insert(event, char, { isDecimalSign, isMinusSign });
+ }
+ }
+
+ onPaste(event: ClipboardEvent) {
+ if (!this.disabled && !this.readonly) {
+ event.preventDefault();
+ let data = (event.clipboardData || (this.document as any).defaultView['clipboardData']).getData('Text');
+ if (data) {
+ if (this.maxlength) {
+ data = data.toString().substring(0, this.maxlength);
+ }
+
+ let filteredData = this.parseValue(data);
+ if (filteredData != null) {
+ this.insert(event, filteredData.toString());
+ }
+ }
+ }
+ }
+
+ allowMinusSign() {
+ return this.min == null || this.min < 0;
+ }
+
+ isMinusSign(char: string) {
+ if (this._minusSign.test(char) || char === '-') {
+ this._minusSign.lastIndex = 0;
+ return true;
+ }
+
+ return false;
+ }
+
+ isDecimalSign(char: string) {
+ if (this._decimal.test(char)) {
+ this._decimal.lastIndex = 0;
+ return true;
+ }
+
+ return false;
+ }
+
+ isDecimalMode() {
+ return this.mode === 'decimal';
+ }
+
+ getDecimalCharIndexes(val: string) {
+ let decimalCharIndex = val.search(this._decimal);
+ this._decimal.lastIndex = 0;
+
+ const filteredVal = val
+ .replace(this._prefix as RegExp, '')
+ .trim()
+ .replace(/\s/g, '')
+ .replace(this._currency as RegExp, '');
+ const decimalCharIndexWithoutPrefix = filteredVal.search(this._decimal);
+ this._decimal.lastIndex = 0;
+
+ return { decimalCharIndex, decimalCharIndexWithoutPrefix };
+ }
+
+ getCharIndexes(val: string) {
+ const decimalCharIndex = val.search(this._decimal);
+ this._decimal.lastIndex = 0;
+ const minusCharIndex = val.search(this._minusSign);
+ this._minusSign.lastIndex = 0;
+ const suffixCharIndex = val.search(this._suffix as RegExp);
+ (this._suffix as RegExp).lastIndex = 0;
+ const currencyCharIndex = val.search(this._currency as RegExp);
+ (this._currency as RegExp).lastIndex = 0;
+
+ return { decimalCharIndex, minusCharIndex, suffixCharIndex, currencyCharIndex };
+ }
+
+ insert(event: Event, text: string, sign = { isDecimalSign: false, isMinusSign: false }) {
+ const minusCharIndexOnText = text.search(this._minusSign);
+ this._minusSign.lastIndex = 0;
+ if (!this.allowMinusSign() && minusCharIndexOnText !== -1) {
+ return;
+ }
+
+ let selectionStart = this.input?.nativeElement.selectionStart;
+ let selectionEnd = this.input?.nativeElement.selectionEnd;
+ let inputValue = this.input?.nativeElement.value.trim();
+ const { decimalCharIndex, minusCharIndex, suffixCharIndex, currencyCharIndex } = this.getCharIndexes(inputValue);
+ let newValueStr;
+
+ if (sign.isMinusSign) {
+ if (selectionStart === 0) {
+ newValueStr = inputValue;
+ if (minusCharIndex === -1 || selectionEnd !== 0) {
+ newValueStr = this.insertText(inputValue, text, 0, selectionEnd);
+ }
+
+ this.updateValue(event, newValueStr, text, 'insert');
+ }
+ } else if (sign.isDecimalSign) {
+ if (decimalCharIndex > 0 && selectionStart === decimalCharIndex) {
+ this.updateValue(event, inputValue, text, 'insert');
+ } else if (decimalCharIndex > selectionStart && decimalCharIndex < selectionEnd) {
+ newValueStr = this.insertText(inputValue, text, selectionStart, selectionEnd);
+ this.updateValue(event, newValueStr, text, 'insert');
+ } else if (decimalCharIndex === -1 && this.maxFractionDigits) {
+ newValueStr = this.insertText(inputValue, text, selectionStart, selectionEnd);
+ this.updateValue(event, newValueStr, text, 'insert');
+ }
+ } else {
+ const maxFractionDigits = this.numberFormat.resolvedOptions().maximumFractionDigits;
+ const operation = selectionStart !== selectionEnd ? 'range-insert' : 'insert';
+
+ if (decimalCharIndex > 0 && selectionStart > decimalCharIndex) {
+ if (selectionStart + text.length - (decimalCharIndex + 1) <= maxFractionDigits) {
+ const charIndex = currencyCharIndex >= selectionStart ? currencyCharIndex - 1 : suffixCharIndex >= selectionStart ? suffixCharIndex : inputValue.length;
+
+ newValueStr = inputValue.slice(0, selectionStart) + text + inputValue.slice(selectionStart + text.length, charIndex) + inputValue.slice(charIndex);
+ this.updateValue(event, newValueStr, text, operation);
+ }
+ } else {
+ newValueStr = this.insertText(inputValue, text, selectionStart, selectionEnd);
+ this.updateValue(event, newValueStr, text, operation);
+ }
+ }
+ }
+
+ insertText(value: string, text: string, start: number, end: number) {
+ let textSplit = text === '.' ? text : text.split('.');
+
+ if (textSplit.length === 2) {
+ const decimalCharIndex = value.slice(start, end).search(this._decimal);
+ this._decimal.lastIndex = 0;
+ return decimalCharIndex > 0 ? value.slice(0, start) + this.formatValue(text) + value.slice(end) : value || this.formatValue(text);
+ } else if (end - start === value.length) {
+ return this.formatValue(text);
+ } else if (start === 0) {
+ return text + value.slice(end);
+ } else if (end === value.length) {
+ return value.slice(0, start) + text;
+ } else {
+ return value.slice(0, start) + text + value.slice(end);
+ }
+ }
+
+ deleteRange(value: string, start: number, end: number) {
+ let newValueStr;
+
+ if (end - start === value.length) newValueStr = '';
+ else if (start === 0) newValueStr = value.slice(end);
+ else if (end === value.length) newValueStr = value.slice(0, start);
+ else newValueStr = value.slice(0, start) + value.slice(end);
+
+ return newValueStr;
+ }
+
+ initCursor() {
+ let selectionStart = this.input?.nativeElement.selectionStart;
+ let selectionEnd = this.input?.nativeElement.selectionEnd;
+ let inputValue = this.input?.nativeElement.value;
+ let valueLength = inputValue.length;
+ let index = null;
+
+ // remove prefix
+ let prefixLength = (this.prefixChar || '').length;
+ inputValue = inputValue.replace(this._prefix, '');
+
+ // Will allow selecting whole prefix. But not a part of it.
+ // Negative values will trigger clauses after this to fix the cursor position.
+ if (selectionStart === selectionEnd || selectionStart !== 0 || selectionEnd < prefixLength) {
+ selectionStart -= prefixLength;
+ }
+
+ let char = inputValue.charAt(selectionStart);
+ if (this.isNumeralChar(char)) {
+ return selectionStart + prefixLength;
+ }
+
+ //left
+ let i = selectionStart - 1;
+ while (i >= 0) {
+ char = inputValue.charAt(i);
+ if (this.isNumeralChar(char)) {
+ index = i + prefixLength;
+ break;
+ } else {
+ i--;
+ }
+ }
+
+ if (index !== null) {
+ this.input?.nativeElement.setSelectionRange(index + 1, index + 1);
+ } else {
+ i = selectionStart;
+ while (i < valueLength) {
+ char = inputValue.charAt(i);
+ if (this.isNumeralChar(char)) {
+ index = i + prefixLength;
+ break;
+ } else {
+ i++;
+ }
+ }
+
+ if (index !== null) {
+ this.input?.nativeElement.setSelectionRange(index, index);
+ }
+ }
+
+ return index || 0;
+ }
+
+ onInputClick() {
+ const currentValue = this.input?.nativeElement.value;
+
+ if (!this.readonly && currentValue !== getSelection()) {
+ this.initCursor();
+ }
+ }
+
+ isNumeralChar(char: string) {
+ if (char.length === 1 && (this._numeral.test(char) || this._decimal.test(char) || this._group.test(char) || this._minusSign.test(char))) {
+ this.resetRegex();
+ return true;
+ }
+
+ return false;
+ }
+
+ resetRegex() {
+ this._numeral.lastIndex = 0;
+ this._decimal.lastIndex = 0;
+ this._group.lastIndex = 0;
+ this._minusSign.lastIndex = 0;
+ }
+
+ updateValue(event: Event, valueStr: Nullable, insertedValueStr: Nullable, operation: Nullable) {
+ let currentValue = this.input?.nativeElement.value;
+ let newValue = null;
+
+ if (valueStr != null) {
+ newValue = this.parseValue(valueStr);
+ newValue = !newValue && !this.allowEmpty ? 0 : newValue;
+ this.updateInput(newValue, insertedValueStr, operation, valueStr);
+
+ this.handleOnInput(event, currentValue, newValue);
+ }
+ }
+
+ handleOnInput(event: Event, currentValue: string, newValue: any) {
+ if (this.isValueChanged(currentValue, newValue)) {
+ (this.input as ElementRef).nativeElement.value = this.formatValue(newValue);
+ this.input?.nativeElement.setAttribute('aria-valuenow', newValue);
+ this.updateModel(event, newValue);
+ this.onInput.emit({ originalEvent: event, value: newValue, formattedValue: currentValue });
+ }
+ }
+
+ isValueChanged(currentValue: string, newValue: string) {
+ if (newValue === null && currentValue !== null) {
+ return true;
+ }
+
+ if (newValue != null) {
+ let parsedCurrentValue = typeof currentValue === 'string' ? this.parseValue(currentValue) : currentValue;
+ return newValue !== parsedCurrentValue;
+ }
+
+ return false;
+ }
+
+ validateValue(value: number | string) {
+ if (value === '-' || value == null) {
+ return null;
+ }
+
+ if (this.min != null && (value as number) < this.min) {
+ return this.min;
+ }
+
+ if (this.max != null && (value as number) > this.max) {
+ return this.max;
+ }
+
+ return value;
+ }
+
+ updateInput(value: any, insertedValueStr: Nullable, operation: Nullable, valueStr: Nullable) {
+ insertedValueStr = insertedValueStr || '';
+
+ let inputValue = this.input?.nativeElement.value;
+ let newValue = this.formatValue(value);
+ let currentLength = inputValue.length;
+
+ if (newValue !== valueStr) {
+ newValue = this.concatValues(newValue, valueStr as string);
+ }
+
+ if (currentLength === 0) {
+ this.input.nativeElement.value = newValue;
+ this.input.nativeElement.setSelectionRange(0, 0);
+ const index = this.initCursor();
+ const selectionEnd = index + insertedValueStr.length;
+ this.input.nativeElement.setSelectionRange(selectionEnd, selectionEnd);
+ } else {
+ let selectionStart = this.input.nativeElement.selectionStart;
+ let selectionEnd = this.input.nativeElement.selectionEnd;
+
+ if (this.maxlength && newValue.length > this.maxlength) {
+ newValue = newValue.slice(0, this.maxlength);
+ selectionStart = Math.min(selectionStart, this.maxlength);
+ selectionEnd = Math.min(selectionEnd, this.maxlength);
+ }
+
+ if (this.maxlength && this.maxlength < newValue.length) {
+ return;
+ }
+
+ this.input.nativeElement.value = newValue;
+ let newLength = newValue.length;
+
+ if (operation === 'range-insert') {
+ const startValue = this.parseValue((inputValue || '').slice(0, selectionStart));
+ const startValueStr = startValue !== null ? startValue.toString() : '';
+ const startExpr = startValueStr.split('').join(`(${this.groupChar})?`);
+ const sRegex = new RegExp(startExpr, 'g');
+ sRegex.test(newValue);
+
+ const tExpr = insertedValueStr.split('').join(`(${this.groupChar})?`);
+ const tRegex = new RegExp(tExpr, 'g');
+ tRegex.test(newValue.slice(sRegex.lastIndex));
+
+ selectionEnd = sRegex.lastIndex + tRegex.lastIndex;
+ this.input.nativeElement.setSelectionRange(selectionEnd, selectionEnd);
+ } else if (newLength === currentLength) {
+ if (operation === 'insert' || operation === 'delete-back-single') this.input.nativeElement.setSelectionRange(selectionEnd + 1, selectionEnd + 1);
+ else if (operation === 'delete-single') this.input.nativeElement.setSelectionRange(selectionEnd - 1, selectionEnd - 1);
+ else if (operation === 'delete-range' || operation === 'spin') this.input.nativeElement.setSelectionRange(selectionEnd, selectionEnd);
+ } else if (operation === 'delete-back-single') {
+ let prevChar = inputValue.charAt(selectionEnd - 1);
+ let nextChar = inputValue.charAt(selectionEnd);
+ let diff = currentLength - newLength;
+ let isGroupChar = this._group.test(nextChar);
+
+ if (isGroupChar && diff === 1) {
+ selectionEnd += 1;
+ } else if (!isGroupChar && this.isNumeralChar(prevChar)) {
+ selectionEnd += -1 * diff + 1;
+ }
+
+ this._group.lastIndex = 0;
+ this.input.nativeElement.setSelectionRange(selectionEnd, selectionEnd);
+ } else if (inputValue === '-' && operation === 'insert') {
+ this.input.nativeElement.setSelectionRange(0, 0);
+ const index = this.initCursor();
+ const selectionEnd = index + insertedValueStr.length + 1;
+ this.input.nativeElement.setSelectionRange(selectionEnd, selectionEnd);
+ } else {
+ selectionEnd = selectionEnd + (newLength - currentLength);
+ this.input.nativeElement.setSelectionRange(selectionEnd, selectionEnd);
+ }
+ }
+
+ this.input.nativeElement.setAttribute('aria-valuenow', value);
+ }
+
+ concatValues(val1: string, val2: string) {
+ if (val1 && val2) {
+ let decimalCharIndex = val2.search(this._decimal);
+ this._decimal.lastIndex = 0;
+
+ if (this.suffixChar) {
+ return decimalCharIndex !== -1 ? val1.replace(this.suffixChar, '').split(this._decimal)[0] + val2.replace(this.suffixChar, '').slice(decimalCharIndex) + this.suffixChar : val1;
+ } else {
+ return decimalCharIndex !== -1 ? val1.split(this._decimal)[0] + val2.slice(decimalCharIndex) : val1;
+ }
+ }
+ return val1;
+ }
+
+ getDecimalLength(value: string) {
+ if (value) {
+ const valueSplit = value.split(this._decimal);
+
+ if (valueSplit.length === 2) {
+ return valueSplit[1]
+ .replace(this._suffix as RegExp, '')
+ .trim()
+ .replace(/\s/g, '')
+ .replace(this._currency as RegExp, '').length;
+ }
+ }
+
+ return 0;
+ }
+
+ onInputFocus(event: Event) {
+ this.focused = true;
+ this.onFocus.emit(event);
+ }
+
+ onInputBlur(event: Event) {
+ this.focused = false;
+
+ const newValueNumber = this.validateValue(this.parseValue(this.input.nativeElement.value));
+ const newValueString = newValueNumber?.toString();
+ this.input.nativeElement.value = this.formatValue(newValueString);
+ this.input.nativeElement.setAttribute('aria-valuenow', newValueString);
+ this.updateModel(event, newValueNumber);
+ this.onBlur.emit(event);
+ }
+
+ formattedValue() {
+ const val = !this.value && !this.allowEmpty ? 0 : this.value;
+ return this.formatValue(val);
+ }
+
+ updateModel(event: Event, value: any) {
+ const isBlurUpdateOnMode = this.ngControl?.control?.updateOn === 'blur';
+
+ if (this.value !== value) {
+ this.value = value;
+
+ if (!(isBlurUpdateOnMode && this.focused)) {
+ this.onModelChange(value);
+ }
+ } else if (isBlurUpdateOnMode) {
+ this.onModelChange(value);
+ }
+ this.onModelTouched();
+ }
+
+ writeValue(value: any): void {
+ this.value = value ? Number(value) : value;
+ this.cd.markForCheck();
+ }
+
+ registerOnChange(fn: Function): void {
+ this.onModelChange = fn;
+ }
+
+ registerOnTouched(fn: Function): void {
+ this.onModelTouched = fn;
+ }
+
+ setDisabledState(val: boolean): void {
+ this.disabled = val;
+ this.cd.markForCheck();
+ }
+
+ get filled() {
+ return this.value != null && this.value.toString().length > 0;
+ }
+
+ clearTimer() {
+ if (this.timer) {
+ clearInterval(this.timer);
+ }
+ }
+}
+
+@NgModule({
+ imports: [InputNumber, SharedModule],
+ exports: [InputNumber, SharedModule]
+})
export class InputNumberModule {}
\ No newline at end of file
diff --git a/UI/src/app/shares/jwt.interceptor.ts b/UI/src/app/shares/jwt.interceptor.ts
index 72e3195..e551bd7 100644
--- a/UI/src/app/shares/jwt.interceptor.ts
+++ b/UI/src/app/shares/jwt.interceptor.ts
@@ -1,47 +1,47 @@
-import { Injectable } from '@angular/core';
-import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpInterceptorFn, HttpHandlerFn } from '@angular/common/http';
-import { Observable } from 'rxjs';
-import { Utils } from './utils';
-/*
-@Injectable()
-export class JwtInterceptor implements HttpInterceptor {
- intercept(request: HttpRequest, next: HttpHandler): Observable> {
- // add authorization header with jwt token if available
- const currentUser = Utils.getCurrentUser();//localStorage.getItem('currentUser')!;
- console.log('I am interceptor debug for currentUser obj', currentUser);
- if (currentUser && currentUser.token) {
- //console.log('I am interceptor add authorization header with jwt token');
- request = request.clone({
- setHeaders: {
- Authorization: `Bearer ${currentUser.token}`
- }
- });
- }
- return next.handle(request);
- }
-}
-
-/*
-new one
-////////////////
-*/
-export const JwtInterceptor: HttpInterceptorFn = (request: HttpRequest, next: HttpHandlerFn): Observable> => {
- //const logger = inject(Logger);
- //logger.log(`Request is on its way to ${req.url}`);
- const currentUser = Utils.getCurrentUser();//localStorage.getItem('currentUser')!;
- // console.log('I am interceptor debug for currentUser obj', currentUser);
- if (currentUser && currentUser.token && currentUser.token != "") {
- // console.log('I am interceptor add authorization header with jwt token');
- request = request.clone({
- setHeaders: {
- Authorization: `Bearer ${currentUser.token}`
- }
- });
- }
- return next(request);
-}
-
-////////////////
-/*
-providers: [provideHttpClient(withInterceptors([loggerInterceptor]))]
+import { Injectable } from '@angular/core';
+import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpInterceptorFn, HttpHandlerFn } from '@angular/common/http';
+import { Observable } from 'rxjs';
+import { Utils } from './utils';
+/*
+@Injectable()
+export class JwtInterceptor implements HttpInterceptor {
+ intercept(request: HttpRequest, next: HttpHandler): Observable> {
+ // add authorization header with jwt token if available
+ const currentUser = Utils.getCurrentUser();//localStorage.getItem('currentUser')!;
+ console.log('I am interceptor debug for currentUser obj', currentUser);
+ if (currentUser && currentUser.token) {
+ //console.log('I am interceptor add authorization header with jwt token');
+ request = request.clone({
+ setHeaders: {
+ Authorization: `Bearer ${currentUser.token}`
+ }
+ });
+ }
+ return next.handle(request);
+ }
+}
+
+/*
+new one
+////////////////
+*/
+export const JwtInterceptor: HttpInterceptorFn = (request: HttpRequest, next: HttpHandlerFn): Observable> => {
+ //const logger = inject(Logger);
+ //logger.log(`Request is on its way to ${req.url}`);
+ const currentUser = Utils.getCurrentUser();//localStorage.getItem('currentUser')!;
+ // console.log('I am interceptor debug for currentUser obj', currentUser);
+ if (currentUser && currentUser.token && currentUser.token != "") {
+ // console.log('I am interceptor add authorization header with jwt token');
+ request = request.clone({
+ setHeaders: {
+ Authorization: `Bearer ${currentUser.token}`
+ }
+ });
+ }
+ return next(request);
+}
+
+////////////////
+/*
+providers: [provideHttpClient(withInterceptors([loggerInterceptor]))]
*/
\ No newline at end of file
diff --git a/UI/src/app/shares/lookup.service.ts b/UI/src/app/shares/lookup.service.ts
index c6bfc3d..600c192 100644
--- a/UI/src/app/shares/lookup.service.ts
+++ b/UI/src/app/shares/lookup.service.ts
@@ -1,136 +1,136 @@
-import { Injectable, Inject , Output} from '@angular/core';
-import { HttpClient, HttpParams } from '@angular/common/http';
-import { map } from 'rxjs/operators';
-import { Observable, of } from 'rxjs';
-import { ConfigureUrl,Lookup,LookupEdit,ResultModel
- } from '../models';
-import { Utils,AppSettingService } from '../shares';
-
-
-@Injectable({ providedIn: 'root' })
-export class LookupService {
- msg: string = "[lookupService] ";
- _lookupByStatus:any;
- _staffList:any;
- _clientList:any;
- _jobList:any;
- constructor(private http: HttpClient,
- private appSetting :AppSettingService
- ) {
- // this default when load look for previous login information
- // if no need then comment out downthere
- this.setAllNull();
- }
- setAllNull() :void {
- // this._lhdList = undefined;
- this._lookupByStatus = {};
- this._staffList = null;
- this._clientList = null;
- this._jobList = null;
- }
- loadLookupEditBystatus(type:string): Observable> {
- const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/LoadLookupEdit";
- const params = new HttpParams().append('type', type);
- console.log(this.msg + "go server this loadLookupEditBystatus is in cache ", type);
- return this.http.get>(baseUrl, {params});
-}
-loadLookupById(id:number, type:string): Observable> {
- const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl;
- const params = new HttpParams()
- .append('id', id)
- .append('type', type);
- return this.http.get>(baseUrl,{params});
- }
- saveLookup(item:LookupEdit): Observable> { //insert Lookup
- this.setAllNull();
- let config = { headers : { 'Content-Type': 'application/json' } };
- const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl;
- return this.http.post>(baseUrl, item, config);
- }
- deleteLookup(id:number): Observable>{
- const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl;
- return this.http.delete>(baseUrl + "/" + id );
- }
-
- loadLookupBystatus(type:string): Observable> {
- const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/LoadJobs";
- console.log(this.msg + "loadLookupBystatus", this._lookupByStatus);
- if (this._lookupByStatus[type])
- {
- console.log(this.msg + "Not go server this LoadJobs is in cache ",type);
- return of(this._lookupByStatus[type]);
- }
- else
- {
- const params = new HttpParams().append('type', type);
- console.log(this.msg + "go server this loadLookupBystatus is in cache ", type);
- return this.http.get>(baseUrl, {params})
- .pipe(map(resultModel => {
- this._lookupByStatus[type] = resultModel;
- return resultModel;
- })
- );
- }
- }
- loadStaffs(): Observable> {
- const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/GetStaffs";
- /* const params = new HttpParams()
- .append('id', id)
- .append('type', type);
- return this.http.get>(baseUrl,{params});
- */
- if (this._staffList != null)
- {
- return of(this._staffList);
- }
- else
- {
- return this.http.get>(baseUrl)
- .pipe(map( x => {
- this._staffList = x;
- return x;
- }));
- }
- }
- loadClients(): Observable> {
- const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/GetClients";
- /* const params = new HttpParams()
- .append('id', id)
- .append('type', type);
- return this.http.get>(baseUrl,{params});
- */
- if (this._clientList != null)
- {
- return of(this._clientList);
- }
- else
- {
- return this.http.get>(baseUrl)
- .pipe(map(resultModel => {
- this._clientList = resultModel;
- return resultModel;
- })
- );
- }
- }
- loadJobs(): Observable> {
- const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/GetJobs";
- /* const params = new HttpParams()
- .append('id', id)
- .append('type', type);
- return this.http.get>(baseUrl,{params});
- */
- if (this._jobList != null)
- {
- return of(this._jobList);
- }
- else
- {
- return this.http.get>(baseUrl)
- .pipe(map(x => {
- this._jobList = x;
- return x;
- }))
- }
- }
-}
+import { Injectable, Inject , Output} from '@angular/core';
+import { HttpClient, HttpParams } from '@angular/common/http';
+import { map } from 'rxjs/operators';
+import { Observable, of } from 'rxjs';
+import { ConfigureUrl,Lookup,LookupEdit,ResultModel
+ } from '../models';
+import { Utils,AppSettingService } from '../shares';
+
+
+@Injectable({ providedIn: 'root' })
+export class LookupService {
+ msg: string = "[lookupService] ";
+ _lookupByStatus:any;
+ _staffList:any;
+ _clientList:any;
+ _jobList:any;
+ constructor(private http: HttpClient,
+ private appSetting :AppSettingService
+ ) {
+ // this default when load look for previous login information
+ // if no need then comment out downthere
+ this.setAllNull();
+ }
+ setAllNull() :void {
+ // this._lhdList = undefined;
+ this._lookupByStatus = {};
+ this._staffList = null;
+ this._clientList = null;
+ this._jobList = null;
+ }
+ loadLookupEditBystatus(type:string): Observable> {
+ const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/LoadLookupEdit";
+ const params = new HttpParams().append('type', type);
+ console.log(this.msg + "go server this loadLookupEditBystatus is in cache ", type);
+ return this.http.get>(baseUrl, {params});
+}
+loadLookupById(id:number, type:string): Observable> {
+ const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl;
+ const params = new HttpParams()
+ .append('id', id)
+ .append('type', type);
+ return this.http.get>(baseUrl,{params});
+ }
+ saveLookup(item:LookupEdit): Observable> { //insert Lookup
+ this.setAllNull();
+ let config = { headers : { 'Content-Type': 'application/json' } };
+ const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl;
+ return this.http.post>(baseUrl, item, config);
+ }
+ deleteLookup(id:number): Observable>{
+ const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl;
+ return this.http.delete>(baseUrl + "/" + id );
+ }
+
+ loadLookupBystatus(type:string): Observable> {
+ const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/LoadJobs";
+ console.log(this.msg + "loadLookupBystatus", this._lookupByStatus);
+ if (this._lookupByStatus[type])
+ {
+ console.log(this.msg + "Not go server this LoadJobs is in cache ",type);
+ return of(this._lookupByStatus[type]);
+ }
+ else
+ {
+ const params = new HttpParams().append('type', type);
+ console.log(this.msg + "go server this loadLookupBystatus is in cache ", type);
+ return this.http.get>(baseUrl, {params})
+ .pipe(map(resultModel => {
+ this._lookupByStatus[type] = resultModel;
+ return resultModel;
+ })
+ );
+ }
+ }
+ loadStaffs(): Observable> {
+ const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/GetStaffs";
+ /* const params = new HttpParams()
+ .append('id', id)
+ .append('type', type);
+ return this.http.get>(baseUrl,{params});
+ */
+ if (this._staffList != null)
+ {
+ return of(this._staffList);
+ }
+ else
+ {
+ return this.http.get>(baseUrl)
+ .pipe(map( x => {
+ this._staffList = x;
+ return x;
+ }));
+ }
+ }
+ loadClients(): Observable> {
+ const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/GetClients";
+ /* const params = new HttpParams()
+ .append('id', id)
+ .append('type', type);
+ return this.http.get>(baseUrl,{params});
+ */
+ if (this._clientList != null)
+ {
+ return of(this._clientList);
+ }
+ else
+ {
+ return this.http.get>(baseUrl)
+ .pipe(map(resultModel => {
+ this._clientList = resultModel;
+ return resultModel;
+ })
+ );
+ }
+ }
+ loadJobs(): Observable> {
+ const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/GetJobs";
+ /* const params = new HttpParams()
+ .append('id', id)
+ .append('type', type);
+ return this.http.get>(baseUrl,{params});
+ */
+ if (this._jobList != null)
+ {
+ return of(this._jobList);
+ }
+ else
+ {
+ return this.http.get>(baseUrl)
+ .pipe(map(x => {
+ this._jobList = x;
+ return x;
+ }))
+ }
+ }
+}
diff --git a/UI/src/app/shares/timeinput.ts b/UI/src/app/shares/timeinput.ts
index 4aedf17..3eace94 100644
--- a/UI/src/app/shares/timeinput.ts
+++ b/UI/src/app/shares/timeinput.ts
@@ -1,149 +1,149 @@
-import { CommonModule } from '@angular/common';
-import { Component, Input, forwardRef, OnInit, OnDestroy } from '@angular/core';
-import { ControlValueAccessor, NG_VALUE_ACCESSOR,ReactiveFormsModule, FormControl, Validators } from '@angular/forms';
-import { Subject, takeUntil } from 'rxjs';
-
-@Component({
- selector: 'time-input',
- imports:[ReactiveFormsModule,CommonModule],
- template: `
-
- `,
- providers: [
- {
- provide: NG_VALUE_ACCESSOR,
- useExisting: forwardRef(() => TimeInputComponent),
- multi: true,
- },
- ],
-})
-export class TimeInputComponent implements ControlValueAccessor, OnInit, OnDestroy {
- @Input() placeholder: string = 'HH:MM';
- @Input() initialValue: string = ''; // Added initialValue input
- @Input() disabled: boolean = false;
-
- timeControl = new FormControl('', [
- Validators.pattern(/^([0-2][0-9]:[0-5][0-9])?$/), // Added more precise regex
- ]);
-
- private destroy$ = new Subject();
- private _value: string = '';
- private onChange: (value: string) => void = () => {};
- private onTouched: () => void = () => {};
-
- constructor() {}
-
- ngOnInit(): void {
- this.timeControl.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((value) => {
- this.onChange(value || ''); // Pass the value to the parent form
- });
- if (this.initialValue) {
- this.writeValue(this.initialValue);
- }
- this.timeControl.disable();
- if(!this.disabled){
- this.timeControl.enable();
- }
- }
-
- ngOnDestroy(): void {
- this.destroy$.next();
- this.destroy$.complete();
- }
-
- get value(): string {
- return this._value;
- }
-
- set value(val: string) {
- this._value = val;
- this.onChange(val);
- this.onTouched();
- }
-
- writeValue(value: string): void {
- this._value = value || '';
- this.formatInput(this._value); // Use the formatting function
- }
-
- registerOnChange(fn: (value: string) => void): void {
- this.onChange = fn;
- }
-
- registerOnTouched(fn: () => void): void {
- this.onTouched = fn;
- }
-
- setDisabledState(isDisabled: boolean): void {
- this.disabled = isDisabled;
- if (isDisabled) {
- this.timeControl.disable();
- } else {
- this.timeControl.enable();
- }
- }
-
- onInput(event: Event): void {
- let input = (event.target as HTMLInputElement).value;
- input = input.replace(/[^0-9]/g, ''); // Remove non-numeric characters.
-
- if (input.length > 4) {
- input = input.slice(0, 4); // Limit to 4 digits
- }
-
- let formatted = '';
- if (input.length > 2) {
- formatted = input.slice(0, 2) + ':' + input.slice(2);
- } else {
- formatted = input;
- }
- this.timeControl.setValue(formatted, { emitEvent: false }); // Prevent infinite loop
- this.value = formatted;
- }
-
- onBlur(): void {
- this.onTouched();
- if (this.timeControl.valid) {
- return;
- }
- if (this.timeControl.value?.length === 0) {
- this.timeControl.setValue('', {emitEvent: false});
- this.value = '';
- return
- }
- this.formatInput(this.timeControl.value);
- }
-
- private formatInput(val: string | null): void {
- if (!val) {
- this.timeControl.setValue('', {emitEvent: false});
- this.value = '';
- return;
- }
- let numbersOnly = val.replace(/[^0-9]/g, '');
- let formatted = '';
-
- if (numbersOnly.length > 2) {
- formatted = numbersOnly.slice(0, 2) + ':' + numbersOnly.slice(2, 4);
- } else {
- formatted = numbersOnly;
- }
- if (formatted.length === 5 && this.timeControl.valid) {
- this.timeControl.setValue(formatted, {emitEvent: false});
- this.value = formatted;
- } else if (numbersOnly.length <= 2) {
- this.timeControl.setValue(formatted, {emitEvent: false});
- this.value = formatted;
- } else {
- this.timeControl.setValue('00:00', {emitEvent: false});
- this.value = '00:00'
- }
- }
-}
+import { CommonModule } from '@angular/common';
+import { Component, Input, forwardRef, OnInit, OnDestroy } from '@angular/core';
+import { ControlValueAccessor, NG_VALUE_ACCESSOR,ReactiveFormsModule, FormControl, Validators } from '@angular/forms';
+import { Subject, takeUntil } from 'rxjs';
+
+@Component({
+ selector: 'time-input',
+ imports:[ReactiveFormsModule,CommonModule],
+ template: `
+
+ `,
+ providers: [
+ {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => TimeInputComponent),
+ multi: true,
+ },
+ ],
+})
+export class TimeInputComponent implements ControlValueAccessor, OnInit, OnDestroy {
+ @Input() placeholder: string = 'HH:MM';
+ @Input() initialValue: string = ''; // Added initialValue input
+ @Input() disabled: boolean = false;
+
+ timeControl = new FormControl('', [
+ Validators.pattern(/^([0-2][0-9]:[0-5][0-9])?$/), // Added more precise regex
+ ]);
+
+ private destroy$ = new Subject();
+ private _value: string = '';
+ private onChange: (value: string) => void = () => {};
+ private onTouched: () => void = () => {};
+
+ constructor() {}
+
+ ngOnInit(): void {
+ this.timeControl.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((value) => {
+ this.onChange(value || ''); // Pass the value to the parent form
+ });
+ if (this.initialValue) {
+ this.writeValue(this.initialValue);
+ }
+ this.timeControl.disable();
+ if(!this.disabled){
+ this.timeControl.enable();
+ }
+ }
+
+ ngOnDestroy(): void {
+ this.destroy$.next();
+ this.destroy$.complete();
+ }
+
+ get value(): string {
+ return this._value;
+ }
+
+ set value(val: string) {
+ this._value = val;
+ this.onChange(val);
+ this.onTouched();
+ }
+
+ writeValue(value: string): void {
+ this._value = value || '';
+ this.formatInput(this._value); // Use the formatting function
+ }
+
+ registerOnChange(fn: (value: string) => void): void {
+ this.onChange = fn;
+ }
+
+ registerOnTouched(fn: () => void): void {
+ this.onTouched = fn;
+ }
+
+ setDisabledState(isDisabled: boolean): void {
+ this.disabled = isDisabled;
+ if (isDisabled) {
+ this.timeControl.disable();
+ } else {
+ this.timeControl.enable();
+ }
+ }
+
+ onInput(event: Event): void {
+ let input = (event.target as HTMLInputElement).value;
+ input = input.replace(/[^0-9]/g, ''); // Remove non-numeric characters.
+
+ if (input.length > 4) {
+ input = input.slice(0, 4); // Limit to 4 digits
+ }
+
+ let formatted = '';
+ if (input.length > 2) {
+ formatted = input.slice(0, 2) + ':' + input.slice(2);
+ } else {
+ formatted = input;
+ }
+ this.timeControl.setValue(formatted, { emitEvent: false }); // Prevent infinite loop
+ this.value = formatted;
+ }
+
+ onBlur(): void {
+ this.onTouched();
+ if (this.timeControl.valid) {
+ return;
+ }
+ if (this.timeControl.value?.length === 0) {
+ this.timeControl.setValue('', {emitEvent: false});
+ this.value = '';
+ return
+ }
+ this.formatInput(this.timeControl.value);
+ }
+
+ private formatInput(val: string | null): void {
+ if (!val) {
+ this.timeControl.setValue('', {emitEvent: false});
+ this.value = '';
+ return;
+ }
+ let numbersOnly = val.replace(/[^0-9]/g, '');
+ let formatted = '';
+
+ if (numbersOnly.length > 2) {
+ formatted = numbersOnly.slice(0, 2) + ':' + numbersOnly.slice(2, 4);
+ } else {
+ formatted = numbersOnly;
+ }
+ if (formatted.length === 5 && this.timeControl.valid) {
+ this.timeControl.setValue(formatted, {emitEvent: false});
+ this.value = formatted;
+ } else if (numbersOnly.length <= 2) {
+ this.timeControl.setValue(formatted, {emitEvent: false});
+ this.value = formatted;
+ } else {
+ this.timeControl.setValue('00:00', {emitEvent: false});
+ this.value = '00:00'
+ }
+ }
+}
diff --git a/UI/src/app/shares/utils.ts b/UI/src/app/shares/utils.ts
index 6c5ac8f..2346d14 100644
--- a/UI/src/app/shares/utils.ts
+++ b/UI/src/app/shares/utils.ts
@@ -1,219 +1,219 @@
-import moment from "moment";
-import { Person, User, userRole } from "../models"
-import { TreeNode } from "primeng/api";
-
-type MyProName = "fatherId" | "motherId";
-
-export class Utils {
- static toBase64 (file:any)
- {
- let promise = new Promise((resolve, reject) => {
- const reader = new FileReader();
- reader.readAsDataURL(file);
- reader.onload = () => resolve(reader.result);
- reader.onerror = reject;
- });
- return promise;
- }
- static getBase64(file: any) {
- let result: any;
- var reader = new FileReader();
- reader.readAsDataURL(file);
- reader.onload = function () {
- result = reader.result;
- console.log('tobase64 ', result);
- };
- reader.onerror = function (error) {
- console.log('Error: ', error);
- };
- }
- static getCurrentUser(): User {
-
- let myUser = new User();
- myUser.username = "";
- const obj = sessionStorage.getItem('currentUser');
- //const obj = localStorage.getItem('currentUser');
- if (obj != undefined) {
- let user = JSON.parse(obj);
- myUser.firstName = user.firstName;
- myUser.id = user.id;
- myUser.lastName = user.lastName;
- myUser.role = user.role;
- myUser.username = user.username;
- myUser.email = user.email;
- myUser.token = user.token;
- myUser.phone = user.phone;
- myUser.department = user.department;
- myUser.position = user.position;
- myUser.managerEmail = user.managerEmail;
-
-
-
- }
- // console.log("[util] getcurrentUser",myUser);
- return myUser;
- }
- static setLocalStore(user: User): void {
- if (user && user.token) {
- // store user details and jwt token in local storage to keep user logged in between page refreshes
- // localStorage.setItem('currentUser', JSON.stringify(user));
- sessionStorage.setItem('currentUser', JSON.stringify(user));
- }
- }
-
- static getLastMonth(): Date {
- let result = moment().subtract(1, 'months').toDate();
- result.setDate(1);
- return result;
- }
- static getHome(): string {
- let result ="";
- const user = this.getCurrentUser();
- const urole = user.role;
- if (urole == userRole.Accounting)
- {
- result = "staffworkw";
- }
- else if (urole == userRole.Admin)
- {
- result = "staff";
- }
- else if (urole == userRole.ServiceManager)
- {
- result = "servicetask";
- }
- else if (urole == userRole.WorkShop)
- {
- result = "staffwork";
- }
- else if (urole == userRole.Normal)
- {
- result = "staff";
- }
- return result;
- }
- static getTimeStr(val:Date): string {
- let result = "00:00";
- if (val)
- {
- const hh = val.getHours();
- const min = val.getMinutes();
- if (hh < 10)
- result = '0' + hh.toString();
- else
- result = hh.toString();
- if (min < 10)
- result = result + ":0" + min.toString();
- else
- result = result + ":" + min.toString();
-
- }
- return result;
- }
- static canRunReport(role: number): boolean {
- let result = false;
- if (role == userRole.Admin)
- result = true;
- return result;
- }
-static getUserRole(user: User): number {
- /*
- sysadmin
- request_manager
- ward_user
- */
- let ret = 0;
- if (user) {
- // console.log("utils user", user);
- if (user.role)
- ret = user.role;
- }
- return ret;
- }
- static getIsAuth(): boolean {
- const user = this.getCurrentUser();
- if (user.username != "") {
- return true;
- }
- else
- return false;
- }
- static getAdminRoleName(): number {
- return userRole.Admin;
- }
- static clearCurrentUser(): void {
- sessionStorage.clear();
- //localStorage.clear();
- }
-
-static formatNode(item:Person): TreeNode
- {
- let label = item.lastName + " " + item.firstName;
- let key = item.id.toString();
- let node: TreeNode ={
- key,
- label,
- type: 'person',
- data: {title: item.title, name: label, image: item.image},
- icon: 'pi pi-user'
- };
-
- return node;
- }
-static getFileExtension(filename: string): string {
- const lastDotIndex = filename.lastIndexOf('.');
- if (lastDotIndex !== -1 && lastDotIndex < filename.length - 1) { // Ensure a dot exists and is not the last character
- return filename.substring(lastDotIndex + 1);
- }
- return ''; // No extension found
-}
- static getChildForParentId(proName: MyProName , pid: number,childressNodes: Person[]): TreeNode[] {
- let result: TreeNode[] =[];
- let tree_node_child: TreeNode[];
- let node:TreeNode;
- let item: Person;
-
- const children = childressNodes.filter(x => x[proName] == pid);
- for (let c = 0; c < children.length; c++)
- {
- item = children[c];
- node = this.formatNode(item);
- tree_node_child = this.getChildForParentId(proName,item.id, childressNodes);
- console.log("getChildForParentId childressNodes item tree_node_child ", childressNodes, item, tree_node_child);
- if (tree_node_child.length > 0)
- {
- node.expanded = true;
- node.children = tree_node_child;
- }
- result.push(node);
- }
- return result;
- }
-
-static populateNode(proName: MyProName, familyList: Person[]): TreeNode[] {
- let familyTree: TreeNode[] = [];
- let node:TreeNode;
- let child_nodes:TreeNode[];
-
- let childressNodes:Person[];
-
- let item: Person;
- const topNodes = familyList.filter(x => x[proName]! < 1);
- childressNodes = familyList.filter(x => x[proName]! > 0);
- for(let i = 0; i < topNodes.length; i++)
- {
- item = topNodes[i];
- node = Utils.formatNode(item);
- child_nodes = this.getChildForParentId(proName, item.id,childressNodes);
- console.log("populate getchildrenForParentId", child_nodes);
- if (child_nodes.length > 0)
- {
- node.expanded = true;
- node.children = child_nodes;
- }
- familyTree.push(node);
- //childressNodes =
- }
- return familyTree;
- }
+import moment from "moment";
+import { Person, User, userRole } from "../models"
+import { TreeNode } from "primeng/api";
+
+type MyProName = "fatherId" | "motherId";
+
+export class Utils {
+ static toBase64 (file:any)
+ {
+ let promise = new Promise((resolve, reject) => {
+ const reader = new FileReader();
+ reader.readAsDataURL(file);
+ reader.onload = () => resolve(reader.result);
+ reader.onerror = reject;
+ });
+ return promise;
+ }
+ static getBase64(file: any) {
+ let result: any;
+ var reader = new FileReader();
+ reader.readAsDataURL(file);
+ reader.onload = function () {
+ result = reader.result;
+ console.log('tobase64 ', result);
+ };
+ reader.onerror = function (error) {
+ console.log('Error: ', error);
+ };
+ }
+ static getCurrentUser(): User {
+
+ let myUser = new User();
+ myUser.username = "";
+ const obj = sessionStorage.getItem('currentUser');
+ //const obj = localStorage.getItem('currentUser');
+ if (obj != undefined) {
+ let user = JSON.parse(obj);
+ myUser.firstName = user.firstName;
+ myUser.id = user.id;
+ myUser.lastName = user.lastName;
+ myUser.role = user.role;
+ myUser.username = user.username;
+ myUser.email = user.email;
+ myUser.token = user.token;
+ myUser.phone = user.phone;
+ myUser.department = user.department;
+ myUser.position = user.position;
+ myUser.managerEmail = user.managerEmail;
+
+
+
+ }
+ // console.log("[util] getcurrentUser",myUser);
+ return myUser;
+ }
+ static setLocalStore(user: User): void {
+ if (user && user.token) {
+ // store user details and jwt token in local storage to keep user logged in between page refreshes
+ // localStorage.setItem('currentUser', JSON.stringify(user));
+ sessionStorage.setItem('currentUser', JSON.stringify(user));
+ }
+ }
+
+ static getLastMonth(): Date {
+ let result = moment().subtract(1, 'months').toDate();
+ result.setDate(1);
+ return result;
+ }
+ static getHome(): string {
+ let result ="";
+ const user = this.getCurrentUser();
+ const urole = user.role;
+ if (urole == userRole.Accounting)
+ {
+ result = "staffworkw";
+ }
+ else if (urole == userRole.Admin)
+ {
+ result = "staff";
+ }
+ else if (urole == userRole.ServiceManager)
+ {
+ result = "servicetask";
+ }
+ else if (urole == userRole.WorkShop)
+ {
+ result = "staffwork";
+ }
+ else if (urole == userRole.Normal)
+ {
+ result = "staff";
+ }
+ return result;
+ }
+ static getTimeStr(val:Date): string {
+ let result = "00:00";
+ if (val)
+ {
+ const hh = val.getHours();
+ const min = val.getMinutes();
+ if (hh < 10)
+ result = '0' + hh.toString();
+ else
+ result = hh.toString();
+ if (min < 10)
+ result = result + ":0" + min.toString();
+ else
+ result = result + ":" + min.toString();
+
+ }
+ return result;
+ }
+ static canRunReport(role: number): boolean {
+ let result = false;
+ if (role == userRole.Admin)
+ result = true;
+ return result;
+ }
+static getUserRole(user: User): number {
+ /*
+ sysadmin
+ request_manager
+ ward_user
+ */
+ let ret = 0;
+ if (user) {
+ // console.log("utils user", user);
+ if (user.role)
+ ret = user.role;
+ }
+ return ret;
+ }
+ static getIsAuth(): boolean {
+ const user = this.getCurrentUser();
+ if (user.username != "") {
+ return true;
+ }
+ else
+ return false;
+ }
+ static getAdminRoleName(): number {
+ return userRole.Admin;
+ }
+ static clearCurrentUser(): void {
+ sessionStorage.clear();
+ //localStorage.clear();
+ }
+
+static formatNode(item:Person): TreeNode
+ {
+ let label = item.lastName + " " + item.firstName;
+ let key = item.id.toString();
+ let node: TreeNode ={
+ key,
+ label,
+ type: 'person',
+ data: {title: item.title, name: label, image: item.image},
+ icon: 'pi pi-user'
+ };
+
+ return node;
+ }
+static getFileExtension(filename: string): string {
+ const lastDotIndex = filename.lastIndexOf('.');
+ if (lastDotIndex !== -1 && lastDotIndex < filename.length - 1) { // Ensure a dot exists and is not the last character
+ return filename.substring(lastDotIndex + 1);
+ }
+ return ''; // No extension found
+}
+ static getChildForParentId(proName: MyProName , pid: number,childressNodes: Person[]): TreeNode[] {
+ let result: TreeNode[] =[];
+ let tree_node_child: TreeNode[];
+ let node:TreeNode;
+ let item: Person;
+
+ const children = childressNodes.filter(x => x[proName] == pid);
+ for (let c = 0; c < children.length; c++)
+ {
+ item = children[c];
+ node = this.formatNode(item);
+ tree_node_child = this.getChildForParentId(proName,item.id, childressNodes);
+ console.log("getChildForParentId childressNodes item tree_node_child ", childressNodes, item, tree_node_child);
+ if (tree_node_child.length > 0)
+ {
+ node.expanded = true;
+ node.children = tree_node_child;
+ }
+ result.push(node);
+ }
+ return result;
+ }
+
+static populateNode(proName: MyProName, familyList: Person[]): TreeNode[] {
+ let familyTree: TreeNode[] = [];
+ let node:TreeNode;
+ let child_nodes:TreeNode[];
+
+ let childressNodes:Person[];
+
+ let item: Person;
+ const topNodes = familyList.filter(x => x[proName]! < 1);
+ childressNodes = familyList.filter(x => x[proName]! > 0);
+ for(let i = 0; i < topNodes.length; i++)
+ {
+ item = topNodes[i];
+ node = Utils.formatNode(item);
+ child_nodes = this.getChildForParentId(proName, item.id,childressNodes);
+ console.log("populate getchildrenForParentId", child_nodes);
+ if (child_nodes.length > 0)
+ {
+ node.expanded = true;
+ node.children = child_nodes;
+ }
+ familyTree.push(node);
+ //childressNodes =
+ }
+ return familyTree;
+ }
}
\ No newline at end of file
diff --git a/UI/src/app/staff/index.ts b/UI/src/app/staff/index.ts
index fe5f350..6861f5a 100644
--- a/UI/src/app/staff/index.ts
+++ b/UI/src/app/staff/index.ts
@@ -1,4 +1,4 @@
-export * from './staff.component';
-export * from './staff.edit.component';
-export * from './staff.service';
-export * from './staff.pass.component';
+export * from './staff.component';
+export * from './staff.edit.component';
+export * from './staff.service';
+export * from './staff.pass.component';
diff --git a/UI/src/app/staff/staff.component.css b/UI/src/app/staff/staff.component.css
index 3db3368..1c17f21 100644
--- a/UI/src/app/staff/staff.component.css
+++ b/UI/src/app/staff/staff.component.css
@@ -1,12 +1,12 @@
-table {
- width: 100%;
-}
-
-.mat-form-field {
- font-size: 14px;
- width: 100%;
-}
-td.mat-column-edit, .mat-column-delete {
- width: 35px;
- padding-right: 2px;
-}
+table {
+ width: 100%;
+}
+
+.mat-form-field {
+ font-size: 14px;
+ width: 100%;
+}
+td.mat-column-edit, .mat-column-delete {
+ width: 35px;
+ padding-right: 2px;
+}
diff --git a/UI/src/app/staff/staff.component.html b/UI/src/app/staff/staff.component.html
index 85d7bce..5a4a995 100644
--- a/UI/src/app/staff/staff.component.html
+++ b/UI/src/app/staff/staff.component.html
@@ -1,64 +1,64 @@
-
-
Staff List
-
-
-
-
-
-
- | Email
- |
- Surname
- |
- First Name
- |
-
- Active |
- Edit |
-
-
-
-
- | {{user.email}} |
- {{user.lastname}} |
- {{user.firstname}} |
-
-
-
- |
-
-
- |
-
-
-
-
-
+
+
Staff List
+
+
+
+
+
+
+ | Email
+ |
+ Surname
+ |
+ First Name
+ |
+
+ Active |
+ Edit |
+
+
+
+
+ | {{user.email}} |
+ {{user.lastname}} |
+ {{user.firstname}} |
+
+
+
+ |
+
+
+ |
+
+
+
+
+
diff --git a/UI/src/app/staff/staff.component.ts b/UI/src/app/staff/staff.component.ts
index e501953..7b3cc8e 100644
--- a/UI/src/app/staff/staff.component.ts
+++ b/UI/src/app/staff/staff.component.ts
@@ -1,162 +1,162 @@
-import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, inject, ChangeDetectorRef} from '@angular/core';
-
-import { StaffView ,StaffSearch } from '../models';
-
-import { Store } from '@ngrx/store';
-import { StaffsActions, StaffsApiActions } from '../state/staff.actions';
-import { take } from 'rxjs/operators';
-import { Subscription } from 'rxjs';
-import { Router } from '@angular/router';
-import { StaffService } from './staff.service';
-import { AuthenticationService } from '../user-services';
-import { TableModule } from 'primeng/table';
-import { FormsModule } from '@angular/forms';
-import { CommonModule } from '@angular/common';
-import { ButtonModule } from 'primeng/button';
-import { InputTextModule } from 'primeng/inputtext';
-import { selectStaffLoaded,selectStaff } from '../state/staff.selectors';
-
-@Component({
- selector: 'staff-list',
- templateUrl: './staff.component.html',
- imports:[TableModule,FormsModule,CommonModule,ButtonModule,InputTextModule],
- styleUrls: ['./staff.component.css'],
- changeDetection: ChangeDetectionStrategy.OnPush
-})
-export class StaffComponent implements OnInit, OnDestroy{
- private readonly store = inject(Store);
- private subscription:Subscription = new Subscription();
- firstname = '';
- email = '';
- lastname = '';
- loading = false;
- userList:StaffView[] = [];
- users = this.store.selectSignal(selectStaff);
- staffloadyet = this.store.selectSignal(selectStaffLoaded)
- private cd = inject(ChangeDetectorRef);
- msg ="[Staff component]";
-
- /*
- private store: Store
- */
- constructor(
- private staffService: StaffService,
- private authenticationService: AuthenticationService,
- private router: Router
- ) {}
- getSearchCiteria(): StaffSearch {
- let criteria:StaffSearch = {
- email: this.email,
- firstName: this.firstname,
- lastName: this.lastname,
- };
-
- return criteria;
-
- }
- canSearch():boolean {
- let result = false;
- result = this.email !== "";
- result = result || this.lastname !== "";
- result = result || this.firstname !== "";
- return result;
- }
- ngOnInit(): void
- {
- this.authenticationService.isHome = false;
- this.authenticationService.isReport = false;
- const prev = this.staffService.searchCriteria;
- let goload = true;
- if (prev.lastName !== '')
- {
- this.lastname = prev.lastName;
- goload = true;
- }
- if (prev.firstName !== '')
- {
- this.firstname = prev.firstName;
- goload = true;
- }
- if (prev.email !== '')
- {
- this.email = prev.email;
- goload = true;
- }
- if (goload)
- {
- this.search();
- }
- }
- getActive(active:boolean):string {
- let result = 'false-icon pi-times-circle';
- if (active)
- result = 'true-icon pi-check-circle';
- return result;
- }
- search():void {
- const canSearch = true; // this.canSearch();
- if (canSearch)
- {
-
- const criteria = this.getSearchCiteria();
- this.staffService.searchCriteria = criteria;
- console.log("search function store staffload yet",this.staffloadyet(), this.users);
- if (this.staffloadyet() != true) {
- this.store.dispatch(StaffsActions.loadStaff({criteria}));
- }
- /*
- if (this.staffloadyet() != true) {
- this.loading = true;
- const loadStaff$ = this.staffService.searchStaffs(criteria);
- this.subscription.add(
- loadStaff$.subscribe( {
- next: result => {
-
- this.loading = false;
-
- this.userList = result.data;
- this.store.dispatch(StaffsApiActions.loadStaffSuccess({staffs:this.userList, staffLoad: true}));
- this.cd.detectChanges();
- },
- error: e => {
- const message = e || e.message;
- // this.toastr.error(message);
- this.loading = false;
- console.log("error ", e);
- }
- })
- );
- }
- */
- }
- }
- newUser():void {
- //console.log("add new employee");
- this.router.navigate( ['/staff/new'], { queryParams: {returnUrl:'/staff' } });
- }
- edit(id: number) : void {
- //console.log("edit staff", id);
- this.router.navigate( ['/staff/'+id], { queryParams: {returnUrl:'/staff' } });
- }
-
- deleteItem(id: number): void {
- this.staffService.deleteStaff(id)
- .pipe(take(1))
- .subscribe({ next: result => {
- console.log(this.msg + " deleteItem success", result);
- //let data = this.dataSource.data;
- //let index: number = data.findIndex(d => d.id === id);
- // console.log(this.msg + "delete from datasource");
- //data.splice(index, 1)
- //this.dataSource.data = data;
- },
- error: e => console.error(e)
- });
- //console.log(this.msg + "click button to delete");
- }
-
- ngOnDestroy() {
- this.subscription.unsubscribe();
- }
-}
-
+import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, inject, ChangeDetectorRef} from '@angular/core';
+
+import { StaffView ,StaffSearch } from '../models';
+
+import { Store } from '@ngrx/store';
+import { StaffsActions, StaffsApiActions } from '../state/staff.actions';
+import { take } from 'rxjs/operators';
+import { Subscription } from 'rxjs';
+import { Router } from '@angular/router';
+import { StaffService } from './staff.service';
+import { AuthenticationService } from '../user-services';
+import { TableModule } from 'primeng/table';
+import { FormsModule } from '@angular/forms';
+import { CommonModule } from '@angular/common';
+import { ButtonModule } from 'primeng/button';
+import { InputTextModule } from 'primeng/inputtext';
+import { selectStaffLoaded,selectStaff } from '../state/staff.selectors';
+
+@Component({
+ selector: 'staff-list',
+ templateUrl: './staff.component.html',
+ imports:[TableModule,FormsModule,CommonModule,ButtonModule,InputTextModule],
+ styleUrls: ['./staff.component.css'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class StaffComponent implements OnInit, OnDestroy{
+ private readonly store = inject(Store);
+ private subscription:Subscription = new Subscription();
+ firstname = '';
+ email = '';
+ lastname = '';
+ loading = false;
+ userList:StaffView[] = [];
+ users = this.store.selectSignal(selectStaff);
+ staffloadyet = this.store.selectSignal(selectStaffLoaded)
+ private cd = inject(ChangeDetectorRef);
+ msg ="[Staff component]";
+
+ /*
+ private store: Store
+ */
+ constructor(
+ private staffService: StaffService,
+ private authenticationService: AuthenticationService,
+ private router: Router
+ ) {}
+ getSearchCiteria(): StaffSearch {
+ let criteria:StaffSearch = {
+ email: this.email,
+ firstName: this.firstname,
+ lastName: this.lastname,
+ };
+
+ return criteria;
+
+ }
+ canSearch():boolean {
+ let result = false;
+ result = this.email !== "";
+ result = result || this.lastname !== "";
+ result = result || this.firstname !== "";
+ return result;
+ }
+ ngOnInit(): void
+ {
+ this.authenticationService.isHome = false;
+ this.authenticationService.isReport = false;
+ const prev = this.staffService.searchCriteria;
+ let goload = true;
+ if (prev.lastName !== '')
+ {
+ this.lastname = prev.lastName;
+ goload = true;
+ }
+ if (prev.firstName !== '')
+ {
+ this.firstname = prev.firstName;
+ goload = true;
+ }
+ if (prev.email !== '')
+ {
+ this.email = prev.email;
+ goload = true;
+ }
+ if (goload)
+ {
+ this.search();
+ }
+ }
+ getActive(active:boolean):string {
+ let result = 'false-icon pi-times-circle';
+ if (active)
+ result = 'true-icon pi-check-circle';
+ return result;
+ }
+ search():void {
+ const canSearch = true; // this.canSearch();
+ if (canSearch)
+ {
+
+ const criteria = this.getSearchCiteria();
+ this.staffService.searchCriteria = criteria;
+ console.log("search function store staffload yet",this.staffloadyet(), this.users);
+ if (this.staffloadyet() != true) {
+ this.store.dispatch(StaffsActions.loadStaff({criteria}));
+ }
+ /*
+ if (this.staffloadyet() != true) {
+ this.loading = true;
+ const loadStaff$ = this.staffService.searchStaffs(criteria);
+ this.subscription.add(
+ loadStaff$.subscribe( {
+ next: result => {
+
+ this.loading = false;
+
+ this.userList = result.data;
+ this.store.dispatch(StaffsApiActions.loadStaffSuccess({staffs:this.userList, staffLoad: true}));
+ this.cd.detectChanges();
+ },
+ error: e => {
+ const message = e || e.message;
+ // this.toastr.error(message);
+ this.loading = false;
+ console.log("error ", e);
+ }
+ })
+ );
+ }
+ */
+ }
+ }
+ newUser():void {
+ //console.log("add new employee");
+ this.router.navigate( ['/staff/new'], { queryParams: {returnUrl:'/staff' } });
+ }
+ edit(id: number) : void {
+ //console.log("edit staff", id);
+ this.router.navigate( ['/staff/'+id], { queryParams: {returnUrl:'/staff' } });
+ }
+
+ deleteItem(id: number): void {
+ this.staffService.deleteStaff(id)
+ .pipe(take(1))
+ .subscribe({ next: result => {
+ console.log(this.msg + " deleteItem success", result);
+ //let data = this.dataSource.data;
+ //let index: number = data.findIndex(d => d.id === id);
+ // console.log(this.msg + "delete from datasource");
+ //data.splice(index, 1)
+ //this.dataSource.data = data;
+ },
+ error: e => console.error(e)
+ });
+ //console.log(this.msg + "click button to delete");
+ }
+
+ ngOnDestroy() {
+ this.subscription.unsubscribe();
+ }
+}
+
diff --git a/UI/src/app/staff/staff.edit.component.css b/UI/src/app/staff/staff.edit.component.css
index 139597f..99a8091 100644
--- a/UI/src/app/staff/staff.edit.component.css
+++ b/UI/src/app/staff/staff.edit.component.css
@@ -1,2 +1,2 @@
-
-
+
+
diff --git a/UI/src/app/staff/staff.edit.component.html b/UI/src/app/staff/staff.edit.component.html
index 0059cbd..ebaeb70 100644
--- a/UI/src/app/staff/staff.edit.component.html
+++ b/UI/src/app/staff/staff.edit.component.html
@@ -1,48 +1,48 @@
-
-
- {{getEditText()}}
-
-
-
+
+
+ {{getEditText()}}
+
+
+
\ No newline at end of file
diff --git a/UI/src/app/staff/staff.edit.component.ts b/UI/src/app/staff/staff.edit.component.ts
index d5d8516..3a32488 100644
--- a/UI/src/app/staff/staff.edit.component.ts
+++ b/UI/src/app/staff/staff.edit.component.ts
@@ -1,214 +1,214 @@
-import { Component, inject, input, numberAttribute, OnDestroy, OnInit } from '@angular/core';
-import { Router, ActivatedRoute } from '@angular/router';
-import { ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms';
-import { Subject, Subscription} from 'rxjs';
-import { MessageService } from 'primeng/api';
-
-import {Staff, Code, userRole} from '../models';
-import { LookupService, Utils } from '../shares';
-import { StaffService } from './staff.service';
-import { ButtonModule } from 'primeng/button';
-import { SelectModule } from 'primeng/select';
-import { CheckboxModule } from 'primeng/checkbox';
-import { InputTextModule } from 'primeng/inputtext';
-
-@Component({
-templateUrl: 'staff.edit.component.html',
-selector: 'staff-edit',
-imports:[ButtonModule,ReactiveFormsModule,SelectModule,CheckboxModule, InputTextModule],
-styleUrls: ['staff.edit.component.css']
-})
-export class StaffEditComponent implements OnInit, OnDestroy {
- returnUrl ='';
- loginUser ='';
- _error ='';
- _id= -1;
- Roles: Code[] = [];
- id = input(0);
- isNew = false;
- validationPoints?: Code[];
- private staffService = inject(StaffService);
- private messageService = inject(MessageService);
- private route = inject(ActivatedRoute);
- private router = inject(Router);
- private lookupService = inject(LookupService);
- msg="[adminUser Component] ";
- private formBuilder = inject(UntypedFormBuilder);
- //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], //Validators.required
- firstname: ['',Validators.required], //Validators.required
- lastname: ['',Validators.required], //Validators.required
- phone: [''], //Validators.required
- password: [''],
- active: [false], //Validators.required
-
- roleType: [0,[Validators.required, Validators.min(1)]],
- });
-constructor() {
- this.fillRole();
- }
-fillRole() : void
-{
- this.Roles = [];
- let item:Code = {id:userRole.Admin, name: 'Admin', status:'Admin'};
- this.Roles.push(item);
- item = {id:userRole.Accounting, name: 'Accounting', status:'Accounting'};
- this.Roles.push(item);
- item = {id:userRole.Normal, name: 'Normal', status:'Normal'};
- this.Roles.push(item);
- item = {id:userRole.ServiceManager, name: 'Service Manager', status:'ServiceManager'};
- this.Roles.push(item);
- //item = {id:userRole.Normal, name: 'Switch', status:'Switch'};
- //this.Roles.push(item);
-}
-
-getClassForRequire(prev:string,name:string): 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;
-}
-
-ngOnInit(): void {
- this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
- const id = this.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.staffService.loadStaffById(id).subscribe({
- next: x => this.assignValue(x.data),
- error: e => console.log(e)
- })
- );
- }
- else
- {
- this.isNew = true;
- }
-}
-
-getEditText(): string {
- if (this._id > 0)
- return "Edit Staff";
- else
- return "New Staff";
-}
-// 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;
- }
-
-assignValue(adminuser:Staff): void {
- this.adminuserForm.patchValue({
- id: adminuser.id,
- email: adminuser.email,
- firstname: adminuser.firstname,
- lastname: adminuser.lastname,
- active: adminuser.active,
- phone: adminuser.phone,
- roleType: adminuser.roleType,
-
- });
- // 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;}
- validate(adminuser:Staff): boolean {
- let result = true;
- if (adminuser.email.trim() == '')
- {
- this._error = 'email is blank or empty';
- result = false;
- }
- else if (adminuser.firstname.trim() == '')
- {
- this._error = 'Firstname is blank or empty';
- result = false;
- }
- else if (adminuser.lastname.trim() == '')
- {
- this._error = 'lastname is blank or empty';
- result = false;
- }
- return result;
- }
-onSubmit(): void {
- // if form valid then go save
- //make sure
- const okToSave = this.adminuserForm.valid;
- if (okToSave)
- {
- let adminuserValue = this.adminuserForm.value;
-
- let adminuser:Staff = {
- id: adminuserValue.id,
- email: adminuserValue.email,
- firstname: adminuserValue.firstname,
- lastname: adminuserValue.lastname,
- active: adminuserValue.active,
- roleType: adminuserValue.roleType,
- phone: adminuserValue.phone,
- password: adminuserValue.password,
-
- type: 0
- };
- this._error ='';
- const allOK = this.validate(adminuser);
- if (allOK)
- {
- this.subscription.add (
- this.staffService.saveStaff(adminuser).subscribe({
- next: x => {
- if (x.statusCode >= 1)
- {
- this.messageService.add({severity:'success', summary: 'Save user', detail: adminuser.firstname + " " + adminuser.lastname });
- this.router.navigate([this.returnUrl]);
- }
- else
- {
- this.messageService.add({severity:'error', summary: 'Error', detail: x.message });
- }
- },
- error: e => {
- const message = e || e.message;
- // this.toastr.error(message);
- console.log("error ", e);
- }
- })
- );
- }
- else
- this.messageService.add({severity:'error', summary: 'Error', detail: this._error });
- }
-}
-goBack(): void {
- this.router.navigate([this.returnUrl]);
-}
-ngOnDestroy() {
- this.subscription.unsubscribe();
-}
-}
+import { Component, inject, input, numberAttribute, OnDestroy, OnInit } from '@angular/core';
+import { Router, ActivatedRoute } from '@angular/router';
+import { ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms';
+import { Subject, Subscription} from 'rxjs';
+import { MessageService } from 'primeng/api';
+
+import {Staff, Code, userRole} from '../models';
+import { LookupService, Utils } from '../shares';
+import { StaffService } from './staff.service';
+import { ButtonModule } from 'primeng/button';
+import { SelectModule } from 'primeng/select';
+import { CheckboxModule } from 'primeng/checkbox';
+import { InputTextModule } from 'primeng/inputtext';
+
+@Component({
+templateUrl: 'staff.edit.component.html',
+selector: 'staff-edit',
+imports:[ButtonModule,ReactiveFormsModule,SelectModule,CheckboxModule, InputTextModule],
+styleUrls: ['staff.edit.component.css']
+})
+export class StaffEditComponent implements OnInit, OnDestroy {
+ returnUrl ='';
+ loginUser ='';
+ _error ='';
+ _id= -1;
+ Roles: Code[] = [];
+ id = input(0);
+ isNew = false;
+ validationPoints?: Code[];
+ private staffService = inject(StaffService);
+ private messageService = inject(MessageService);
+ private route = inject(ActivatedRoute);
+ private router = inject(Router);
+ private lookupService = inject(LookupService);
+ msg="[adminUser Component] ";
+ private formBuilder = inject(UntypedFormBuilder);
+ //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], //Validators.required
+ firstname: ['',Validators.required], //Validators.required
+ lastname: ['',Validators.required], //Validators.required
+ phone: [''], //Validators.required
+ password: [''],
+ active: [false], //Validators.required
+
+ roleType: [0,[Validators.required, Validators.min(1)]],
+ });
+constructor() {
+ this.fillRole();
+ }
+fillRole() : void
+{
+ this.Roles = [];
+ let item:Code = {id:userRole.Admin, name: 'Admin', status:'Admin'};
+ this.Roles.push(item);
+ item = {id:userRole.Accounting, name: 'Accounting', status:'Accounting'};
+ this.Roles.push(item);
+ item = {id:userRole.Normal, name: 'Normal', status:'Normal'};
+ this.Roles.push(item);
+ item = {id:userRole.ServiceManager, name: 'Service Manager', status:'ServiceManager'};
+ this.Roles.push(item);
+ //item = {id:userRole.Normal, name: 'Switch', status:'Switch'};
+ //this.Roles.push(item);
+}
+
+getClassForRequire(prev:string,name:string): 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;
+}
+
+ngOnInit(): void {
+ this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
+ const id = this.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.staffService.loadStaffById(id).subscribe({
+ next: x => this.assignValue(x.data),
+ error: e => console.log(e)
+ })
+ );
+ }
+ else
+ {
+ this.isNew = true;
+ }
+}
+
+getEditText(): string {
+ if (this._id > 0)
+ return "Edit Staff";
+ else
+ return "New Staff";
+}
+// 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;
+ }
+
+assignValue(adminuser:Staff): void {
+ this.adminuserForm.patchValue({
+ id: adminuser.id,
+ email: adminuser.email,
+ firstname: adminuser.firstname,
+ lastname: adminuser.lastname,
+ active: adminuser.active,
+ phone: adminuser.phone,
+ roleType: adminuser.roleType,
+
+ });
+ // 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;}
+ validate(adminuser:Staff): boolean {
+ let result = true;
+ if (adminuser.email.trim() == '')
+ {
+ this._error = 'email is blank or empty';
+ result = false;
+ }
+ else if (adminuser.firstname.trim() == '')
+ {
+ this._error = 'Firstname is blank or empty';
+ result = false;
+ }
+ else if (adminuser.lastname.trim() == '')
+ {
+ this._error = 'lastname is blank or empty';
+ result = false;
+ }
+ return result;
+ }
+onSubmit(): void {
+ // if form valid then go save
+ //make sure
+ const okToSave = this.adminuserForm.valid;
+ if (okToSave)
+ {
+ let adminuserValue = this.adminuserForm.value;
+
+ let adminuser:Staff = {
+ id: adminuserValue.id,
+ email: adminuserValue.email,
+ firstname: adminuserValue.firstname,
+ lastname: adminuserValue.lastname,
+ active: adminuserValue.active,
+ roleType: adminuserValue.roleType,
+ phone: adminuserValue.phone,
+ password: adminuserValue.password,
+
+ type: 0
+ };
+ this._error ='';
+ const allOK = this.validate(adminuser);
+ if (allOK)
+ {
+ this.subscription.add (
+ this.staffService.saveStaff(adminuser).subscribe({
+ next: x => {
+ if (x.statusCode >= 1)
+ {
+ this.messageService.add({severity:'success', summary: 'Save user', detail: adminuser.firstname + " " + adminuser.lastname });
+ this.router.navigate([this.returnUrl]);
+ }
+ else
+ {
+ this.messageService.add({severity:'error', summary: 'Error', detail: x.message });
+ }
+ },
+ error: e => {
+ const message = e || e.message;
+ // this.toastr.error(message);
+ console.log("error ", e);
+ }
+ })
+ );
+ }
+ else
+ this.messageService.add({severity:'error', summary: 'Error', detail: this._error });
+ }
+}
+goBack(): void {
+ this.router.navigate([this.returnUrl]);
+}
+ngOnDestroy() {
+ this.subscription.unsubscribe();
+}
+}
diff --git a/UI/src/app/staff/staff.pass.component.css b/UI/src/app/staff/staff.pass.component.css
index 139597f..99a8091 100644
--- a/UI/src/app/staff/staff.pass.component.css
+++ b/UI/src/app/staff/staff.pass.component.css
@@ -1,2 +1,2 @@
-
-
+
+
diff --git a/UI/src/app/staff/staff.pass.component.html b/UI/src/app/staff/staff.pass.component.html
index 6486375..d11b4f0 100644
--- a/UI/src/app/staff/staff.pass.component.html
+++ b/UI/src/app/staff/staff.pass.component.html
@@ -1,24 +1,24 @@
-
-
- {{getEditText()}}
-
-
-
+
+
+ {{getEditText()}}
+
+