import { Component, EventEmitter, inject, Input, Output, SimpleChanges } from '@angular/core'; import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; 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'; import { Country, TravelEntry, TravelPlan } from '../../core/models/carnet/travel-plan'; import { AngularMaterialModule } from '../../shared/module/angular-material.module'; import { CommonModule } from '@angular/common'; import { MatListModule } from '@angular/material/list'; import { MatDialog } from '@angular/material/dialog'; import { ConfirmDialogComponent } from '../../shared/components/confirm-dialog/confirm-dialog.component'; @Component({ selector: 'app-travel-plan', imports: [AngularMaterialModule, ReactiveFormsModule, CommonModule, MatListModule], templateUrl: './travel-plan.component.html', styleUrl: './travel-plan.component.scss' }) export class TravelPlanComponent { @Input() headerid: number = 0; @Input() isViewMode = false; @Input() applicationType: 'new' | 'edit' | 'additional' | 'duplicate' | 'extend' | null = 'edit'; @Output() completed = new EventEmitter(); @Output() needsInsurance = new EventEmitter(); private fb = inject(FormBuilder); private dialog = inject(MatDialog); private travelPlanService = inject(TravelPlanService); private notificationService = inject(NotificationService); private errorHandler = inject(ApiErrorHandlerService); travelForm: FormGroup; isLoading = false; changeInProgress = false; showAdditionalSetsValidationMessage: boolean = false; visitsCount = 1; transitsCount = 1; countries: Country[] = []; selectedVisitCountries: TravelEntry[] = []; selectedTransitCountries: TravelEntry[] = []; // Currently selected country in the listbox selectedAvailableVisitCountry: Country | null = null; selectedAvailableTransitCountry: Country | null = null; selectedVisitCountry: Country | null = null; selectedTransitCountry: Country | null = null; constructor() { this.travelForm = this.fb.group({ usaEntries: ['', [Validators.required, Validators.min(1), Validators.max(99)]], visitsInput: ['', [Validators.min(1), Validators.max(99)]], transitsInput: ['', [Validators.min(1), Validators.max(99)]], }); } ngOnInit(): void { if (this.headerid) { this.loadTravelPlan(); } } ngOnChanges(changes: SimpleChanges) { if (changes['headerid'] && this.headerid > 0) { this.loadTravelPlan(); } } onAvailableVisitCountrySelection(event: any) { this.selectedAvailableVisitCountry = event.value; } onVisitCountrySelection(event: any) { this.selectedVisitCountry = event.value; } onAvailableTransitCountrySelection(event: any) { this.selectedAvailableTransitCountry = event.value; } onTransitCountrySelection(event: any) { this.selectedTransitCountry = event.value; } // Add selected country to visits addVisitCountry(): void { if (!this.selectedAvailableVisitCountry) return; if (this.selectedAvailableVisitCountry.actionType === 'FYI') { this.fyiAction(this.selectedAvailableVisitCountry); } else if (this.selectedAvailableVisitCountry.actionType === 'WARNING') { this.warningAction(this.selectedAvailableVisitCountry); } else if (this.selectedAvailableVisitCountry.actionType === 'ACTION') { this.takeAction(this.selectedAvailableVisitCountry); } else { this.addVisitCountryToCollection(); } } addVisitCountryToCollection(): void { if (!this.selectedAvailableVisitCountry) return; const existingIndex = this.selectedVisitCountries.findIndex( c => c.value === this.selectedAvailableVisitCountry!.value ); if (existingIndex === -1) { this.selectedVisitCountries.push({ ...this.selectedAvailableVisitCountry, visits: this.visitsCount }); if (this.applicationType === 'additional') { this.needsInsurance.emit(this.selectedVisitCountries.length > 0); } } this.visitsCount = 1; } fyiAction(country: Country): void { const dialogRef = this.dialog.open(ConfirmDialogComponent, { width: '560px', data: { title: 'For your information', message: country.actionMessage, confirmText: 'Ok', cancelText: 'Cancel' } }); dialogRef.afterClosed().subscribe(result => { if (result) { this.addVisitCountryToCollection(); } }); } warningAction(country: Country): void { const dialogRef = this.dialog.open(ConfirmDialogComponent, { width: '560px', data: { title: 'Warning', message: country.actionMessage, confirmText: 'Ok', cancelText: 'Cancel' } }); dialogRef.afterClosed().subscribe(result => { if (result) { this.addVisitCountryToCollection(); } }); } takeAction(country: Country): void { if (this.IsCountryExists('transit', country)) { this.addVisitCountryToCollection(); return; } const dialogRef = this.dialog.open(ConfirmDialogComponent, { width: '560px', data: { title: 'Act', message: country.actionMessage, confirmText: 'Ok', cancelText: 'Cancel' } }); dialogRef.afterClosed().subscribe(result => { if (result) { this.transitsCount = this.visitsCount; this.selectedAvailableTransitCountry = this.selectedAvailableVisitCountry; this.addVisitCountryToCollection(); this.addTransitCountryToCollection(); } }); } // Add selected country to transits addTransitCountry(): void { if (!this.selectedAvailableTransitCountry) return; this.addTransitCountryToCollection(); } addTransitCountryToCollection(): void { if (!this.selectedAvailableTransitCountry) return; const existingIndex = this.selectedTransitCountries.findIndex( c => c.value === this.selectedAvailableTransitCountry!.value ); if (existingIndex === -1) { this.selectedTransitCountries.push({ ...this.selectedAvailableTransitCountry, visits: this.transitsCount }); } this.transitsCount = 1; } IsCountryExists(countryType: string, country: Country): boolean { const existingIndex = countryType === 'transit' ? this.selectedTransitCountries.findIndex( c => c.value === country!.value ) : this.selectedVisitCountries.findIndex( c => c.value === country!.value ); return existingIndex > -1; } // Remove country from visits removeVisitCountry(): void { if (!this.selectedVisitCountry) return; this.selectedVisitCountries = this.selectedVisitCountries.filter( c => c.value !== this.selectedVisitCountry!.value ); if (this.applicationType === 'additional') { this.needsInsurance.emit(this.selectedVisitCountries.length > 0); } } // Remove country from transits removeTransitCountry(): void { if (!this.selectedTransitCountry) return; this.selectedTransitCountries = this.selectedTransitCountries.filter( c => c.value !== this.selectedTransitCountry!.value ); } // Calculate total visits get totalVisits(): number { return this.selectedVisitCountries.reduce((sum, country) => sum + country.visits, 0); } // Calculate total transits get totalTransits(): number { return this.selectedTransitCountries.reduce((sum, country) => sum + country.visits, 0); } // Check if a country is already selected (visit) isVisitCountrySelected(country: Country): boolean { return this.selectedVisitCountries.some(c => c.value === country.value) } // Check if a country is already selected (transit) isTransitCountrySelected(country: Country): boolean { return this.selectedTransitCountries.some(c => c.value === country.value); } loadTravelPlan(): void { this.isLoading = true; forkJoin({ countries: this.travelPlanService.getCountriesAndMessages(), travelPlanData: this.travelPlanService.getTravelPlan(this.headerid) }).pipe(finalize(() => { this.isLoading = false; })).subscribe({ next: (results) => { this.countries = results.countries as Country[]; this.patchTravelPlanData(results.travelPlanData); }, 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); } }); } private patchTravelPlanData(plan: TravelPlan): void { this.selectedVisitCountries = plan?.entries; this.selectedTransitCountries = plan?.transits; if (this.applicationType === 'additional') { this.needsInsurance.emit(this.selectedVisitCountries.length > 0) } // Patch form values this.travelForm.patchValue({ usaEntries: plan?.usSets === 0 ? '' : plan?.usSets, visitsInput: 1, transitsInput: 1 // Reset to default }); if (this.isViewMode) { this.travelForm.disable(); } if (this.applicationType === 'additional') { const usentriesControl = this.travelForm.get('usaEntries'); usentriesControl?.setValidators([Validators.min(1), Validators.max(99)]); usentriesControl?.updateValueAndValidity(); } this.completed.emit(this.travelForm.valid || this.travelForm.disabled || (this.applicationType === 'additional' && (this.travelForm.value.usaEntries || this.selectedVisitCountries.length > 0 || this.selectedTransitCountries.length > 0))); } onSubmit(): void { if (this.travelForm.invalid) { this.travelForm.markAllAsTouched(); return; } this.showAdditionalSetsValidationMessage = false; if (this.applicationType === 'additional' && (!this.travelForm.value.usaEntries && this.selectedVisitCountries.length === 0 && this.selectedTransitCountries.length === 0 )) { this.showAdditionalSetsValidationMessage = true; return; } const travelPlan: TravelPlan = { usSets: this.travelForm.value.usaEntries, entries: this.selectedVisitCountries, transits: this.selectedTransitCountries }; 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); }, 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); } }); } }