diff --git a/src/app/carnet/add/add-carnet.component.html b/src/app/carnet/add/add-carnet.component.html index 2e6da70..431e46c 100644 --- a/src/app/carnet/add/add-carnet.component.html +++ b/src/app/carnet/add/add-carnet.component.html @@ -41,8 +41,7 @@ Shipping & Payment + [applicationName]="applicationName" [enableSubmitButton]="allSectionsCompleted"> diff --git a/src/app/carnet/add/add-carnet.component.ts b/src/app/carnet/add/add-carnet.component.ts index c092d38..85277e5 100644 --- a/src/app/carnet/add/add-carnet.component.ts +++ b/src/app/carnet/add/add-carnet.component.ts @@ -1,4 +1,4 @@ -import { Component, inject } from '@angular/core'; +import { Component, inject, ViewChild } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; import { AngularMaterialModule } from '../../shared/module/angular-material.module'; import { CommonModule } from '@angular/common'; @@ -27,7 +27,6 @@ export class AddCarnetComponent { headerid: number = 0; applicationName: string = ''; userPreferences: UserPreferences; - refreshShippingData: boolean = false; allSectionsCompleted: boolean = false; // Track completion of each step @@ -39,6 +38,9 @@ export class AddCarnetComponent { shipping: false }; + @ViewChild(ShippingComponent, { static: false }) + private shippingComponent!: ShippingComponent; + constructor(userPrefenceService: UserPreferencesService) { this.userPreferences = userPrefenceService.getPreferences(); } @@ -60,7 +62,9 @@ export class AddCarnetComponent { } onHolderSelectionUpdated(updated: boolean): void { - this.refreshShippingData = updated; + if (updated) { + this.shippingComponent?.refreshShippingData(); + } } onGoodsSectionSaved(completed: boolean): void { diff --git a/src/app/carnet/edit/edit-carnet.component.html b/src/app/carnet/edit/edit-carnet.component.html index 10901ec..d9aabc0 100644 --- a/src/app/carnet/edit/edit-carnet.component.html +++ b/src/app/carnet/edit/edit-carnet.component.html @@ -41,8 +41,7 @@ Shipping & Payment + [applicationName]="applicationName" [enableSubmitButton]="allSectionsCompleted"> diff --git a/src/app/carnet/edit/edit-carnet.component.ts b/src/app/carnet/edit/edit-carnet.component.ts index 6cd0bf4..5cb1750 100644 --- a/src/app/carnet/edit/edit-carnet.component.ts +++ b/src/app/carnet/edit/edit-carnet.component.ts @@ -1,5 +1,5 @@ import { CommonModule } from '@angular/common'; -import { Component, inject } from '@angular/core'; +import { Component, inject, ViewChild } from '@angular/core'; import { AngularMaterialModule } from '../../shared/module/angular-material.module'; import { ReactiveFormsModule } from '@angular/forms'; import { ApplicationComponent } from '../application/application.component'; @@ -27,9 +27,11 @@ export class EditCarnetComponent { headerid: number = 0; userPreferences: UserPreferences; applicationName: string = ''; - refreshShippingData: boolean = false; allSectionsCompleted: boolean = false; + @ViewChild(ShippingComponent, { static: false }) + private shippingComponent!: ShippingComponent; + // Track completion of each step stepsCompleted = { applicationDetail: true, @@ -75,7 +77,10 @@ export class EditCarnetComponent { } onHolderSelectionUpdated(updated: boolean): void { - this.refreshShippingData = updated; + if (updated) { + this.shippingComponent?.refreshShippingData(); + } + this.isAllSectionsCompleted(); } diff --git a/src/app/carnet/goods/goods.component.html b/src/app/carnet/goods/goods.component.html index 9dc83db..efe4539 100644 --- a/src/app/carnet/goods/goods.component.html +++ b/src/app/carnet/goods/goods.component.html @@ -219,7 +219,8 @@
- @@ -228,7 +229,8 @@
-
diff --git a/src/app/carnet/goods/goods.component.ts b/src/app/carnet/goods/goods.component.ts index 0da5680..1f4647d 100644 --- a/src/app/carnet/goods/goods.component.ts +++ b/src/app/carnet/goods/goods.component.ts @@ -16,7 +16,7 @@ import { ApiErrorHandlerService } from '../../core/services/common/api-error-han import { Country } from '../../core/models/country'; import { UnitOfMeasure } from '../../core/models/unitofmeasure'; import { CommonService } from '../../core/services/common/common.service'; -import { Subject, takeUntil } from 'rxjs'; +import { finalize, Subject, takeUntil } from 'rxjs'; import { Goods, GoodsItem } from '../../core/models/carnet/goods'; @Component({ @@ -47,6 +47,7 @@ export class GoodsComponent { isEditing = false; currentItem: GoodsItem | null = null; isLoading = false; + changeInProgress = false; showItemForm = false; fileToUpload: File | null = null; isProcessing = false; @@ -84,7 +85,7 @@ export class GoodsComponent { weight: [0, [Validators.required, Validators.min(1), Validators.pattern(/^\d+(\.\d{1,4})?$/)]], unitOfMeasure: ['', Validators.required], value: [0, [Validators.required, Validators.min(1), Validators.pattern(/^\d+(\.\d{1,2})?$/)]], - countryOfOrigin: ['', Validators.required] + countryOfOrigin: ['US', Validators.required] }); } @@ -98,18 +99,19 @@ export class GoodsComponent { this.loadUnitOfMeasures(); if (this.headerid > 0) { - this.goodsService.getGoodDetailsByHeaderId(this.headerid).subscribe({ + this.isLoading = true; + this.goodsService.getGoodDetailsByHeaderId(this.headerid).pipe(finalize(() => { + this.isLoading = false; + })).subscribe({ next: (goodsDetail: Goods) => { if (goodsDetail) { this.patchFormData(goodsDetail); this.completed.emit(this.goodsForm.valid && this.dataSource.data.length > 0); } - this.isLoading = false; }, error: (error: any) => { let errorMessage = this.errorHandler.handleApiError(error, 'Failed to load good details'); this.notificationService.showError(errorMessage); - this.isLoading = false; console.error('Error loading good details:', error); } }); @@ -121,23 +123,23 @@ export class GoodsComponent { loadGoodsItems(): void { this.isLoading = true; - this.goodsService.getGoodsItemsByHeaderId(this.headerid).subscribe({ + this.goodsService.getGoodsItemsByHeaderId(this.headerid).pipe(finalize(() => { + this.isLoading = false; + })).subscribe({ next: (items: GoodsItem[]) => { this.dataSource.data = items; - this.isLoading = false; this.completed.emit(this.goodsForm.valid && this.dataSource.data.length > 0); }, error: (error: any) => { let errorMessage = this.errorHandler.handleApiError(error, 'Failed to load goods items'); this.notificationService.showError(errorMessage); - this.isLoading = false; console.error('Error loading goods items:', error); } }); } loadCountries(): void { - this.commonService.getCountries(0) + this.commonService.getCountries() .pipe(takeUntil(this.destroy$)) .subscribe({ next: (countries) => { @@ -145,7 +147,6 @@ export class GoodsComponent { }, error: (error) => { console.error('Failed to load countries', error); - this.isLoading = false; } }); } @@ -156,11 +157,9 @@ export class GoodsComponent { .subscribe({ next: (units) => { this.unitsOfMeasures = units; - this.isLoading = false; }, error: (error) => { console.error('Failed to load unit of measures', error); - this.isLoading = false; } }); } @@ -184,7 +183,8 @@ export class GoodsComponent { this.itemForm.reset({ pieces: 0, weight: 0, - value: 0 + value: 0, + countryOfOrigin: 'US', }); this.itemForm.markAsUntouched(); } @@ -220,11 +220,15 @@ export class GoodsComponent { let itemDataArray: GoodsItem[] = []; itemDataArray.push(itemData); + this.changeInProgress = true; + const saveObservable = this.isEditing && this.currentItem ? this.goodsService.updateGoodsItem(this.headerid, itemDataArray) : this.goodsService.addGoodsItem(this.headerid, itemDataArray); - saveObservable.subscribe({ + saveObservable.pipe(finalize(() => { + this.changeInProgress = false; + })).subscribe({ next: () => { this.notificationService.showSuccess(`Goods ${this.isEditing ? 'updated' : 'added'} successfully`); this.loadGoodsItems(); @@ -275,10 +279,10 @@ export class GoodsComponent { const file: File = event.target.files[0]; this.processExcelFile(file) - .then(response => { - console.log('File uploaded successfully:', response); - // Handle success (e.g., update UI) - }) + // .then(response => { + // // console.log('File uploaded successfully:', response); + // // Handle success (e.g., update UI) + // }) .catch(error => { console.error('Error processing file:', error); this.notificationService.showError('Failed to upload file'); @@ -319,7 +323,10 @@ export class GoodsComponent { // item.unitOfMeasure = this.getUnitOfMeasureValue(item.unitOfMeasure); // }); - this.goodsService.addGoodsItem(this.headerid, items).subscribe({ + this.changeInProgress = true; + this.goodsService.addGoodsItem(this.headerid, items).pipe(finalize(() => { + this.changeInProgress = false; + })).subscribe({ next: () => { this.notificationService.showSuccess(`Goods uploaded successfully`); this.loadGoodsItems(); @@ -390,9 +397,11 @@ export class GoodsComponent { } const formData = this.goodsForm.value; - this.isLoading = true; + this.changeInProgress = true; - this.goodsService.saveGoodsData(this.headerid, formData).subscribe({ + this.goodsService.saveGoodsData(this.headerid, formData).pipe(finalize(() => { + this.changeInProgress = false; + })).subscribe({ next: () => { this.notificationService.showSuccess('Goods information saved successfully'); this.completed.emit(this.goodsForm.valid && this.dataSource.data.length > 0); @@ -401,9 +410,6 @@ export class GoodsComponent { let errorMessage = this.errorHandler.handleApiError(error, 'Failed to save goods information'); this.notificationService.showError(errorMessage); console.error('Error saving goods:', error); - }, - complete: () => { - this.isLoading = false; } }); } diff --git a/src/app/carnet/shipping/shipping.component.html b/src/app/carnet/shipping/shipping.component.html index 7d8b37f..86ce007 100644 --- a/src/app/carnet/shipping/shipping.component.html +++ b/src/app/carnet/shipping/shipping.component.html @@ -1,4 +1,8 @@
+
+ +
+
@@ -344,9 +348,9 @@ Submit Application -
diff --git a/src/app/carnet/shipping/shipping.component.ts b/src/app/carnet/shipping/shipping.component.ts index 488950b..65d9815 100644 --- a/src/app/carnet/shipping/shipping.component.ts +++ b/src/app/carnet/shipping/shipping.component.ts @@ -1,5 +1,5 @@ import { CommonModule } from '@angular/common'; -import { Component, EventEmitter, inject, Input, Output, SimpleChanges } from '@angular/core'; +import { Component, EventEmitter, inject, Input, Output } from '@angular/core'; import { ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms'; import { NotificationService } from '../../core/services/common/notification.service'; import { AngularMaterialModule } from '../../shared/module/angular-material.module'; @@ -34,7 +34,6 @@ import { FormOfSecurity } from '../../core/models/formofsecurity'; export class ShippingComponent { @Input() headerid: number = 0; @Input() isEditMode = false; - @Input() refreshShippingData = false; @Input() enableSubmitButton = false; @Input() applicationName: string = ''; @@ -53,6 +52,7 @@ export class ShippingComponent { shippingForm: FormGroup; isLoading = false; + changeInProgress = false; showAddressForm = false; showContactForm = false; deliveryEstimate: string = ''; @@ -91,7 +91,7 @@ export class ShippingComponent { address2: [''], city: [''], state: [''], - country: [''], + country: ['US'], zip: [''], }), contact: this.fb.group({ @@ -142,7 +142,7 @@ export class ShippingComponent { return; } - this.isLoading = true; + this.changeInProgress = true; const shippingData: Shipping = this.shippingForm.value; if (shippingData.shipTo !== '3RDPARTY') { @@ -150,16 +150,16 @@ export class ShippingComponent { shippingData.contact = shippingData.shipTo === 'PREPARER' ? this.preparerContact ?? undefined : this.holderContact ?? undefined; } - this.shippingService.saveShippingDetails(this.headerid, shippingData).subscribe({ + this.shippingService.saveShippingDetails(this.headerid, shippingData).pipe(finalize(() => { + this.changeInProgress = false; + })).subscribe({ next: () => { this.notificationService.showSuccess('Shipping & payments information saved successfully'); this.completed.emit(true); - this.isLoading = false; }, error: (error) => { let errorMessage = this.errorHandler.handleApiError(error, 'Failed to save shipping and payment information'); this.notificationService.showError(errorMessage); - this.isLoading = false; } }); } @@ -188,8 +188,8 @@ export class ShippingComponent { this.shippingForm.get('address.zip')?.updateValueAndValidity(); } - ngOnChanges(changes: SimpleChanges) { - if (changes['refreshShippingData'] && this.headerid > 0) { + public refreshShippingData(): void { + if (this.headerid > 0) { this.loadShippingData(); } } @@ -209,7 +209,8 @@ export class ShippingComponent { cancelEditAddressForm(): void { this.showAddressForm = false; - this.shippingForm.get('address')?.reset(); + this.shippingForm.get('address')?.reset({ country: 'US' }); + this.loadStates('US'); } onShipToChange(event: any): void { @@ -221,14 +222,15 @@ export class ShippingComponent { if (shipTo === '3RDPARTY') { this.shippingForm.get('contact')?.reset(); - this.shippingForm.get('address')?.reset(); + this.shippingForm.get('address')?.reset({ country: 'US' }); + this.loadStates('US'); this.showAddressForm = true; this.showContactForm = true; } } loadCountries(): void { - this.commonService.getCountries(0) + this.commonService.getCountries() .pipe(takeUntil(this.destroy$)) .subscribe({ next: (countries) => { @@ -236,13 +238,12 @@ export class ShippingComponent { }, error: (error) => { console.error('Failed to load countries', error); - this.isLoading = false; } }); } loadDeliveryTypes(): void { - this.commonService.getDeliveryTypes(0) + this.commonService.getDeliveryTypes() .pipe(takeUntil(this.destroy$)) .subscribe({ next: (deliveryTypes) => { @@ -250,13 +251,12 @@ export class ShippingComponent { }, error: (error) => { console.error('Failed to load delivery types', error); - this.isLoading = false; } }); } loadDeliveryMethods(): void { - this.commonService.getDeliveryMethods(0) + this.commonService.getDeliveryMethods() .pipe(takeUntil(this.destroy$)) .subscribe({ next: (deliveryMethods) => { @@ -264,13 +264,12 @@ export class ShippingComponent { }, error: (error) => { console.error('Failed to load delivery methods', error); - this.isLoading = false; } }); } loadPaymentTypes(): void { - this.commonService.getPaymentTypes(0) + this.commonService.getPaymentTypes() .pipe(takeUntil(this.destroy$)) .subscribe({ next: (paymentTypes) => { @@ -278,7 +277,6 @@ export class ShippingComponent { }, error: (error) => { console.error('Failed to load payment types', error); - this.isLoading = false; } }); } @@ -286,29 +284,29 @@ export class ShippingComponent { loadStates(country: string): void { this.isLoading = true; country = this.countriesHasStates.includes(country) ? country : 'FN'; - this.commonService.getStates(country, 0) - .pipe(takeUntil(this.destroy$)) - .subscribe({ - next: (states) => { - this.states = states; - const stateControl = this.shippingForm.get('contact.state'); - if (this.countriesHasStates.includes(country)) { - stateControl?.enable(); - } else { - stateControl?.disable(); - stateControl?.setValue('FN'); + this.commonService.getStates(country) + .pipe(takeUntil(this.destroy$), + finalize(() => { + this.isLoading = false; + })).subscribe({ + next: (states) => { + this.states = states; + const stateControl = this.shippingForm.get('contact.state'); + if (this.countriesHasStates.includes(country)) { + stateControl?.enable(); + } else { + stateControl?.disable(); + stateControl?.setValue('FN'); + } + }, + error: (error) => { + console.error('Failed to load states', error); } - this.isLoading = false; - }, - error: (error) => { - console.error('Failed to load states', error); - this.isLoading = false; - } - }); + }); } loadFormOfSecurities(): void { - this.commonService.getFormOfSecurities(0) + this.commonService.getFormOfSecurities() .pipe(takeUntil(this.destroy$)) .subscribe({ next: (fos) => { @@ -483,11 +481,11 @@ export class ShippingComponent { } else if (key === 'title') { contactGroup.get(key)?.setValidators([Validators.required, Validators.maxLength(100)]); } else if (key === 'phone') { - contactGroup.get(key)?.setValidators([Validators.required, Validators.pattern(/^[0-9]{10,15}$/)]); + contactGroup.get(key)?.setValidators([Validators.required, Validators.pattern(/^[0-9\-\(\)]{10,15}$/)]); } else if (key === 'mobile') { - contactGroup.get(key)?.setValidators([Validators.required, Validators.pattern(/^[0-9]{10,15}$/)]); + contactGroup.get(key)?.setValidators([Validators.required, Validators.pattern(/^[0-9\-\(\)]{10,15}$/)]); } else if (key === 'fax') { - contactGroup.get(key)?.setValidators([Validators.pattern(/^[0-9]{10,15}$/)]); + contactGroup.get(key)?.setValidators([Validators.pattern(/^[0-9\-\(\)]{10,15}$/)]); } else if (key === 'email') { contactGroup.get(key)?.setValidators([Validators.required, Validators.email, Validators.maxLength(100)]); } diff --git a/src/app/carnet/terms-conditions/terms-conditions.component.html b/src/app/carnet/terms-conditions/terms-conditions.component.html index 9bc63ac..e18b6ba 100644 --- a/src/app/carnet/terms-conditions/terms-conditions.component.html +++ b/src/app/carnet/terms-conditions/terms-conditions.component.html @@ -102,7 +102,8 @@ - diff --git a/src/app/carnet/terms-conditions/terms-conditions.component.ts b/src/app/carnet/terms-conditions/terms-conditions.component.ts index 0b7b3f0..67aece0 100644 --- a/src/app/carnet/terms-conditions/terms-conditions.component.ts +++ b/src/app/carnet/terms-conditions/terms-conditions.component.ts @@ -12,6 +12,7 @@ import { NotificationService } from '../../core/services/common/notification.ser import { StorageService } from '../../core/services/common/storage.service'; import { CarnetService } from '../../core/services/carnet/carnet.service'; import { Fees } from '../../core/models/carnet/fee'; +import { finalize } from 'rxjs'; @Component({ selector: 'app-terms-conditions', @@ -29,7 +30,7 @@ export class TermsConditionsComponent { currentDate = new Date(); hasReadAllTerms = false; currentApplicationDetails: { headerid: number, applicationName: string } | null = null; - isLoading: boolean = false; + changeInProgress: boolean = false; estimatedFees: Fees = {}; private notificationService = inject(NotificationService); @@ -61,19 +62,19 @@ export class TermsConditionsComponent { onAccept(): void { - this.isLoading = true; - this.carnetService.submitApplication(this.currentApplicationDetails?.headerid!).subscribe({ + this.changeInProgress = true; + this.carnetService.submitApplication(this.currentApplicationDetails?.headerid!).pipe(finalize(() => { + this.changeInProgress = false; + })).subscribe({ next: () => { this.notificationService.showSuccess('Application submitted successfully'); this.storageService.removeItem('currentapplication'); this.navigationService.navigate(["home"]); - this.isLoading = false; }, error: (error) => { let errorMessage = this.errorHandler.handleApiError(error, 'Failed to submit application'); this.notificationService.showError(errorMessage); console.error('Error submitting the application', error); - this.isLoading = false; } }); } diff --git a/src/app/carnet/travel-plan/travel-plan.component.html b/src/app/carnet/travel-plan/travel-plan.component.html index 658f00a..72d17fe 100644 --- a/src/app/carnet/travel-plan/travel-plan.component.html +++ b/src/app/carnet/travel-plan/travel-plan.component.html @@ -1,4 +1,7 @@
+
+ +
@@ -139,9 +142,8 @@
-
diff --git a/src/app/carnet/travel-plan/travel-plan.component.ts b/src/app/carnet/travel-plan/travel-plan.component.ts index 66b60d3..bace1b3 100644 --- a/src/app/carnet/travel-plan/travel-plan.component.ts +++ b/src/app/carnet/travel-plan/travel-plan.component.ts @@ -1,6 +1,6 @@ import { Component, EventEmitter, inject, Input, Output, SimpleChanges } from '@angular/core'; import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; -import { forkJoin } from 'rxjs'; +import { finalize, forkJoin } from 'rxjs'; import { TravelPlanService } from '../../core/services/carnet/travel-plan.service'; import { ApiErrorHandlerService } from '../../core/services/common/api-error-handler.service'; import { NotificationService } from '../../core/services/common/notification.service'; @@ -30,6 +30,7 @@ export class TravelPlanComponent { travelForm: FormGroup; isLoading = false; + changeInProgress = false; visitsCount = 1; transitsCount = 1; @@ -253,17 +254,17 @@ export class TravelPlanComponent { forkJoin({ countries: this.travelPlanService.getCountriesAndMessages(), travelPlanData: this.travelPlanService.getTravelPlan(this.headerid) - }).subscribe({ + }).pipe(finalize(() => { + this.isLoading = false; + })).subscribe({ next: (results) => { this.countries = results.countries as Country[]; this.patchTravelPlanData(results.travelPlanData); - this.isLoading = false; }, error: (error) => { console.error('Error loading travel plan data', error); let errorMessage = this.errorHandler.handleApiError(error, 'Failed to load travel plan data'); this.notificationService.showError(errorMessage); - this.isLoading = false; } }); } @@ -295,18 +296,19 @@ export class TravelPlanComponent { transits: this.selectedTransitCountries }; - this.isLoading = true; - this.travelPlanService.saveTravelPlan(this.headerid, travelPlan).subscribe({ + this.changeInProgress = true; + + this.travelPlanService.saveTravelPlan(this.headerid, travelPlan).pipe(finalize(() => { + this.changeInProgress = false; + })).subscribe({ next: () => { this.notificationService.showSuccess('Travel plan saved successfully'); this.completed.emit(true); - this.isLoading = false; }, error: (error) => { let errorMessage = this.errorHandler.handleApiError(error, 'Failed to save travel plan'); this.notificationService.showError(errorMessage); console.error('Error saving travel plan data : ', error); - this.isLoading = false; } }); } diff --git a/src/app/common/footer/footer.component.html b/src/app/common/footer/footer.component.html index 0a8b626..01d4752 100644 --- a/src/app/common/footer/footer.component.html +++ b/src/app/common/footer/footer.component.html @@ -7,7 +7,7 @@ \ No newline at end of file diff --git a/src/app/common/footer/footer.component.ts b/src/app/common/footer/footer.component.ts index 2f94b31..ba744bb 100644 --- a/src/app/common/footer/footer.component.ts +++ b/src/app/common/footer/footer.component.ts @@ -1,6 +1,8 @@ -import { Component, Input } from '@angular/core'; +import { Component, effect, inject, Input } from '@angular/core'; import { AngularMaterialModule } from '../../shared/module/angular-material.module'; import { CommonModule } from '@angular/common'; +import { User } from '../../core/models/user'; +import { UserService } from '../../core/services/common/user.service'; @Component({ selector: 'app-footer', @@ -10,5 +12,20 @@ import { CommonModule } from '@angular/common'; }) export class FooterComponent { currentYear = new Date().getFullYear(); + currentServiceProviderName: string = ''; + userDetails: User | null = {}; + @Input() isUserLoggedIn = false; + + private userService = inject(UserService); + + constructor( + ) { + effect(() => { + this.userDetails = this.userService.userDetailsSignal(); + if (this.userDetails?.userDetails) { + this.currentServiceProviderName = this.userDetails.userDetails.serviceProviderName || ''; + } + }); + } } \ No newline at end of file diff --git a/src/app/common/secured-header/secured-header.component.html b/src/app/common/secured-header/secured-header.component.html index b9c6d42..354784e 100644 --- a/src/app/common/secured-header/secured-header.component.html +++ b/src/app/common/secured-header/secured-header.component.html @@ -6,18 +6,7 @@ diff --git a/src/app/holder/basic-details/basic-details.component.ts b/src/app/holder/basic-details/basic-details.component.ts index 9400cad..3af8d21 100644 --- a/src/app/holder/basic-details/basic-details.component.ts +++ b/src/app/holder/basic-details/basic-details.component.ts @@ -8,7 +8,7 @@ import { NotificationService } from '../../core/services/common/notification.ser import { ApiErrorHandlerService } from '../../core/services/common/api-error-handler.service'; import { ZipCodeValidator } from '../../shared/validators/zipcode-validator'; import { CommonService } from '../../core/services/common/common.service'; -import { Subject, takeUntil } from 'rxjs'; +import { finalize, Subject, takeUntil } from 'rxjs'; import { Country } from '../../core/models/country'; import { BasicDetailService } from '../../core/services/holder/basic-detail.service'; import { BasicDetail } from '../../core/models/holder/basic-detail'; @@ -27,7 +27,6 @@ export class BasicDetailComponent implements OnInit, OnDestroy { @Input() holderid: number = 0; @Output() holderIdCreated = new EventEmitter(); - // @Output() holderName = new EventEmitter(); basicDetailsForm: FormGroup; countries: Country[] = []; @@ -36,6 +35,7 @@ export class BasicDetailComponent implements OnInit, OnDestroy { currentApplicationDetails: { headerid: number, applicationName: string } | null = null; isLoading = false; + changeInProgress = false; countriesHasStates = ['US', 'CA', 'MX']; private destroy$ = new Subject(); @@ -60,12 +60,12 @@ export class BasicDetailComponent implements OnInit, OnDestroy { holderNumber: ['', [Validators.required, Validators.maxLength(20)]], holderType: ['', [Validators.required, Validators.maxLength(20)]], uscibMember: [false, [Validators.required]], - // govAgency: [false, [Validators.required]], + // govAgency: [false, [Validators.required]], address1: ['', [Validators.required, Validators.maxLength(100)]], address2: ['', Validators.maxLength(100)], city: ['', [Validators.required, Validators.maxLength(50)]], state: ['', Validators.required], - country: ['', Validators.required], + country: ['US', Validators.required], zip: ['', [Validators.required, ZipCodeValidator('country')]] }); } @@ -75,19 +75,21 @@ export class BasicDetailComponent implements OnInit, OnDestroy { if (this.holderid > 0) { this.isLoading = true; - this.basicDetailService.getBasicDetailByHolderId(this.holderid).subscribe({ + this.basicDetailService.getBasicDetailByHolderId(this.holderid).pipe(finalize(() => { + this.isLoading = false; + })).subscribe({ next: (basicDetail: BasicDetail) => { this.patchFormData(basicDetail); - // this.holderName.emit(basicDetail.holderName); - this.isLoading = false; + // this.holderName.emit(basicDetail.holderName); }, error: (error: any) => { let errorMessage = this.errorHandler.handleApiError(error, 'Failed to load basic details'); this.notificationService.showError(errorMessage); - this.isLoading = false; console.error('Error loading basic details:', error); } }); + } else { + this.loadStates('US'); // Load states for default country } } @@ -118,12 +120,20 @@ export class BasicDetailComponent implements OnInit, OnDestroy { ? this.basicDetailService.updateBasicDetails(this.holderid, basicDetailData) : this.basicDetailService.createBasicDetail(basicDetailData); - saveObservable.subscribe({ + this.changeInProgress = true; + + saveObservable.pipe(finalize(() => { + this.changeInProgress = false; + })).subscribe({ next: (basicData: any) => { this.notificationService.showSuccess(`Basic details ${this.isEditMode ? 'updated' : 'added'} successfully`); if (!this.isEditMode) { this.holderIdCreated.emit(basicData.HOLDERID); + + // change to edit mode after creation + this.isEditMode = true; + this.holderid = basicData.HOLDERID; } // if (this.isEditMode) { @@ -156,7 +166,7 @@ export class BasicDetailComponent implements OnInit, OnDestroy { holderNumber: data.holderNumber, holderType: data.holderType, uscibMember: data.uscibMember, - // govAgency: data.govAgency, + // govAgency: data.govAgency, address1: data.address1, address2: data.address2, city: data.city, @@ -176,34 +186,29 @@ export class BasicDetailComponent implements OnInit, OnDestroy { loadLookupData(): void { - this.commonService.getCountries(0) + this.commonService.getCountries() .pipe(takeUntil(this.destroy$)) .subscribe({ next: (countries) => { this.countries = countries; - this.isLoading = false; }, error: (error) => { console.error('Failed to load countries', error); - this.isLoading = false; } }); } loadStates(country: string): void { - this.isLoading = true; country = this.countriesHasStates.includes(country) ? country : 'FN'; - this.commonService.getStates(country, this.holderid) + this.commonService.getStates(country) .pipe(takeUntil(this.destroy$)) .subscribe({ next: (states) => { this.states = states; this.updateStateControl('state', country); - this.isLoading = false; }, error: (error) => { console.error('Failed to load states', error); - this.isLoading = false; } }); } diff --git a/src/app/holder/contacts/contacts.component.html b/src/app/holder/contacts/contacts.component.html index b37ee3c..a685f12 100644 --- a/src/app/holder/contacts/contacts.component.html +++ b/src/app/holder/contacts/contacts.component.html @@ -256,7 +256,7 @@
- diff --git a/src/app/holder/contacts/contacts.component.ts b/src/app/holder/contacts/contacts.component.ts index 0516477..2a0c7f2 100644 --- a/src/app/holder/contacts/contacts.component.ts +++ b/src/app/holder/contacts/contacts.component.ts @@ -16,6 +16,7 @@ import { MatDialog } from '@angular/material/dialog'; import { ConfirmDialogComponent } from '../../shared/components/confirm-dialog/confirm-dialog.component'; import { NavigationService } from '../../core/services/common/navigation.service'; import { StorageService } from '../../core/services/common/storage.service'; +import { finalize } from 'rxjs'; @Component({ selector: 'app-contacts', @@ -41,6 +42,7 @@ export class ContactsComponent { isEditing = false; currentContactId: number | null = null; isLoading = false; + changeInProgress = false; showForm = false; showInactiveContacts = false; contacts: Contact[] = []; @@ -67,9 +69,9 @@ export class ContactsComponent { lastName: ['', [Validators.required, Validators.maxLength(50)]], middleInitial: ['', [Validators.maxLength(1)]], title: ['', [Validators.required, Validators.maxLength(100)]], - phone: ['', [Validators.required, Validators.pattern(/^[0-9]{10,15}$/)]], - mobile: ['', [Validators.required, Validators.pattern(/^[0-9]{10,15}$/)]], - fax: ['', [Validators.pattern(/^[0-9]{10,15}$/)]], + phone: ['', [Validators.required, Validators.pattern(/^[0-9\-\(\)]{10,15}$/)]], + mobile: ['', [Validators.required, Validators.pattern(/^[0-9\-\(\)]{10,15}$/)]], + fax: ['', [Validators.pattern(/^[0-9\-\(\)]{10,15}$/)]], email: ['', [Validators.required, Validators.email, Validators.maxLength(100)]], }); @@ -90,16 +92,16 @@ export class ContactsComponent { loadContacts(): void { this.isLoading = true; - this.contactService.getContactsById(this.holderid).subscribe({ + this.contactService.getContactsById(this.holderid).pipe(finalize(() => { + this.isLoading = false; + })).subscribe({ next: (contacts: Contact[]) => { this.contacts = contacts; this.renderContacts(); - this.isLoading = false; }, error: (error: any) => { let errorMessage = this.errorHandler.handleApiError(error, 'Failed to load contacts'); this.notificationService.showError(errorMessage); - this.isLoading = false; console.error('Error loading contacts:', error); } }); @@ -146,7 +148,11 @@ export class ContactsComponent { ? this.contactService.updateContact(this.currentContactId!, contactData) : this.contactService.createContact(this.holderid, contactData); - saveObservable.subscribe({ + this.changeInProgress = true; + + saveObservable.pipe(finalize(() => { + this.changeInProgress = false; + })).subscribe({ next: () => { this.notificationService.showSuccess(`Contact ${this.isEditing ? 'updated' : 'added'} successfully`); this.loadContacts(); diff --git a/src/app/holder/search/search-holder.component.html b/src/app/holder/search/search-holder.component.html index e20a677..51f0b0b 100644 --- a/src/app/holder/search/search-holder.component.html +++ b/src/app/holder/search/search-holder.component.html @@ -117,7 +117,7 @@
-
diff --git a/src/app/holder/search/search-holder.component.ts b/src/app/holder/search/search-holder.component.ts index c48c2b0..526775f 100644 --- a/src/app/holder/search/search-holder.component.ts +++ b/src/app/holder/search/search-holder.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, inject, Input, Output, ViewChild } from '@angular/core'; +import { Component, EventEmitter, inject, Input, Output, SimpleChanges, ViewChild } from '@angular/core'; import { AngularMaterialModule } from '../../shared/module/angular-material.module'; import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { CommonModule } from '@angular/common'; @@ -18,6 +18,7 @@ import { HolderFilter } from '../../core/models/holder/holder-filter'; import { ConfirmDialogComponent } from '../../shared/components/confirm-dialog/confirm-dialog.component'; import { MatDialog } from '@angular/material/dialog'; import { StorageService } from '../../core/services/common/storage.service'; +import { finalize } from 'rxjs'; @Component({ selector: 'app-holder-search', @@ -27,11 +28,16 @@ import { StorageService } from '../../core/services/common/storage.service'; providers: [{ provide: MatPaginatorIntl, useClass: CustomPaginator }], }) export class SearchHolderComponent { + private _paginator!: MatPaginator; @ViewChild(MatPaginator, { static: false }) set paginator(value: MatPaginator) { + this._paginator = value; this.dataSource.paginator = value; } + get paginator(): MatPaginator { + return this._paginator; + } @ViewChild(MatSort, { static: false }) set sort(value: MatSort) { @@ -47,6 +53,7 @@ export class SearchHolderComponent { showInactiveHolders: boolean = false; holders: BasicDetail[] = [] isLoading: boolean = false; + changeInProgress: boolean = false; userPreferences: UserPreferences; searchForm: FormGroup; @@ -62,7 +69,8 @@ export class SearchHolderComponent { dataSource = new MatTableDataSource([]); ngAfterViewInit() { - this.dataSource.paginator = this.paginator; + // This is different from other pages to show the item selected in the table. + //this.dataSource.paginator = this.paginator; this.dataSource.sort = this.sort; } @@ -85,14 +93,25 @@ export class SearchHolderComponent { this.searchHolders(); } + ngOnChanges(changes: SimpleChanges): void { + if (changes['selectedHolderId'] && this.paginator) { + if (this.selectedHolderId && this.dataSource.data.length) { + this.goToItemPage(this.selectedHolderId); + } + } + } + onSearch(): void { this.searchHolders(); } saveHolderSelection(): void { + this.changeInProgress = true; const saveObservable = this.carnetHolderService.saveApplicationHolder(this.headerid, this.selectedHolderId ? Number(this.selectedHolderId) : 0); - saveObservable.subscribe({ + saveObservable.pipe(finalize(() => { + this.changeInProgress = false; + })).subscribe({ next: (basicData: any) => { this.notificationService.showSuccess(`Holder updated successfully`); this.holderSelectionCompleted.emit(true); @@ -114,16 +133,16 @@ export class SearchHolderComponent { this.isLoading = true; const filterData: HolderFilter = this.searchForm.value; - this.holderService.getHolders(filterData).subscribe({ + this.holderService.getHolders(filterData).pipe(finalize(() => { + this.isLoading = false; + })).subscribe({ next: (holders: BasicDetail[]) => { this.dataSource.data = this.holders = holders; this.renderHolders() - this.isLoading = false; }, error: (error: any) => { let errorMessage = this.errorHandler.handleApiError(error, 'Failed to search holders'); this.notificationService.showError(errorMessage); - this.isLoading = false; console.error('Error loading holders:', error); } }); @@ -222,8 +241,25 @@ export class SearchHolderComponent { getAddressLabel(holder: BasicDetail): string { - return `${holder.address1}, ${holder.address2} - ${holder.city}, ${holder.state}, ${holder.zip}, - ${holder.country}`; + const parts = [ + holder.address1, + holder.address2, + holder.city, + holder.state, + holder.zip, + holder.country + ]; + + // Filter out any empty, null, or undefined parts and then join them. + return parts.filter(part => part).join(', '); + } + + goToItemPage(holderid: number): void { + const itemIndex = this.dataSource.data.findIndex(dataItem => dataItem.holderid === holderid); + if (itemIndex > -1 && this.paginator) { + const targetPageIndex = Math.floor(itemIndex / this.paginator.pageSize); + this.paginator.pageIndex = targetPageIndex; + this.dataSource.paginator = this.paginator; + } } }