client-app/src/app/carnet/travel-plan/travel-plan.component.ts

354 lines
11 KiB
TypeScript

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<boolean>();
@Output() needsInsurance = new EventEmitter<boolean>();
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);
}
});
}
}