import { Component, EventEmitter, inject, Input, Output, ViewChild } from '@angular/core'; import { AngularMaterialModule } from '../../shared/module/angular-material.module'; import { CommonModule } from '@angular/common'; import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; import { MatPaginator, MatPaginatorIntl } from '@angular/material/paginator'; import { MatSort } from '@angular/material/sort'; import { MatTableDataSource } from '@angular/material/table'; import { UserPreferences } from '../../core/models/user-preference'; import { Location } from '../../core/models/preparer/location'; import { LocationService } from '../../core/services/preparer/location.service'; import { NotificationService } from '../../core/services/common/notification.service'; import { ApiErrorHandlerService } from '../../core/services/common/api-error-handler.service'; import { CustomPaginator } from '../../shared/custom-paginator'; import { ZipCodeValidator } from '../../shared/validators/zipcode-validator'; import { Country } from '../../core/models/country'; import { State } from '../../core/models/state'; import { finalize, Subject, takeUntil } from 'rxjs'; import { CommonService } from '../../core/services/common/common.service'; @Component({ selector: 'app-location', imports: [AngularMaterialModule, CommonModule, ReactiveFormsModule], templateUrl: './location.component.html', styleUrl: './location.component.scss', providers: [{ provide: MatPaginatorIntl, useClass: CustomPaginator }], }) export class LocationComponent { @ViewChild(MatPaginator, { static: false }) set paginator(value: MatPaginator) { this.dataSource.paginator = value; } @ViewChild(MatSort) sort!: MatSort; displayedColumns: string[] = ['name', 'address', 'city', 'state', 'country', 'actions']; dataSource = new MatTableDataSource(); locationForm: FormGroup; isEditing = false; currentLocationId: number | null = null; isLoading = false; changeInProgress = false; showForm = false; countries: Country[] = []; states: State[] = []; locationReadOnlyFields: any = { lastChangedDate: null, lastChangedBy: null, isInactive: null, inactivatedDate: null }; @Input() clientid: number = 0; @Input() userPreferences: UserPreferences = {}; @Output() hasLocations = new EventEmitter(); @Output() updated = new EventEmitter(); countriesHasStates = ['US', 'CA', 'MX']; private destroy$ = new Subject(); private fb = inject(FormBuilder); private locationService = inject(LocationService); private notificationService = inject(NotificationService); private errorHandler = inject(ApiErrorHandlerService); private commonService = inject(CommonService); constructor() { this.locationForm = this.fb.group({ name: ['', [Validators.required, Validators.maxLength(100)]], address1: ['', [Validators.required, Validators.maxLength(100)]], address2: ['', [Validators.maxLength(100)]], city: ['', [Validators.required, Validators.maxLength(50)]], state: ['', Validators.required], country: ['US', Validators.required], zip: ['', [Validators.required, ZipCodeValidator('country')]], }); } ngOnInit(): void { this.loadCountries(); this.loadLocations(); } ngAfterViewInit() { this.dataSource.paginator = this.paginator; this.dataSource.sort = this.sort; } ngOnDestroy(): void { this.destroy$.next(); this.destroy$.complete(); } public refreshLocationData(): void { this.loadLocations(); } loadLocations(): void { this.isLoading = true; this.locationService.getLocationsById(this.clientid).pipe(finalize(() => { this.isLoading = false; })).subscribe({ next: (locations: Location[]) => { this.dataSource.data = locations; }, error: (error: any) => { let errorMessage = this.errorHandler.handleApiError(error, 'Failed to load locations'); this.notificationService.showError(errorMessage); console.error('Error loading locations:', error); } }); } onCountryChange(country: string): void { this.locationForm.get('state')?.reset(); if (country) { this.loadStates(country); } this.locationForm.get('zip')?.updateValueAndValidity(); } addNewLocation(): void { this.showForm = true; this.isEditing = false; this.currentLocationId = null; this.locationForm.reset(); this.locationForm.patchValue({ country: 'US' }); this.loadStates('US'); } editLocation(location: Location): void { this.showForm = true; this.isEditing = true; this.currentLocationId = location.locationid; this.locationForm.patchValue({ name: location.name, address1: location.address1, address2: location.address2, city: location.city, country: location.country, state: location.state, zip: location.zip, }); if (location.country) { this.loadStates(location.country); } this.locationReadOnlyFields.lastChangedDate = location.lastUpdatedDate ?? location.dateCreated; this.locationReadOnlyFields.lastChangedBy = location.lastUpdatedBy ?? location.createdBy; this.locationReadOnlyFields.isInactive = location.isInactive; this.locationReadOnlyFields.inactivatedDate = location.inactivatedDate; } saveLocation(): void { if (this.locationForm.invalid) { this.locationForm.markAllAsTouched(); return; } // default the first location const locationData: Location = this.locationForm.value; const saveObservable = this.isEditing && (this.currentLocationId! > 0) ? this.locationService.updateLocation(this.currentLocationId!, locationData) : this.locationService.createLocation(this.clientid, locationData); this.changeInProgress = true; saveObservable.pipe(finalize(() => this.changeInProgress = false)).subscribe({ next: () => { this.notificationService.showSuccess(`Location ${this.isEditing ? 'updated' : 'added'} successfully`); this.updated.emit(true); // to update dependent data this.loadLocations(); this.cancelEdit(); this.hasLocations.emit(true); }, error: (error) => { let errorMessage = this.errorHandler.handleApiError(error, `Failed to ${this.isEditing ? 'update' : 'add'} location`); this.notificationService.showError(errorMessage); console.error('Error saving location:', error); } }); } loadCountries(): void { this.commonService.getCountries() .pipe(takeUntil(this.destroy$)) .subscribe({ next: (countries) => { this.countries = countries; }, error: (error) => { console.error('Failed to load countries', error); } }); } loadStates(country: string): void { this.isLoading = true; country = this.countriesHasStates.includes(country) ? country : 'FN'; this.commonService.getStates(country) .pipe(takeUntil(this.destroy$), finalize(() => { this.isLoading = false; })) .subscribe({ next: (states) => { this.states = states; const stateControl = this.locationForm.get('state'); if (this.countriesHasStates.includes(country)) { stateControl?.enable(); } else { stateControl?.disable(); stateControl?.setValue('FN'); } }, error: (error) => { console.error('Failed to load states', error); } }); } // deleteLocation(locationId: string): void { // const dialogRef = this.dialog.open(ConfirmDialogComponent, { // width: '350px', // data: { // title: 'Confirm Delete', // message: 'Are you sure you want to delete this location?', // confirmText: 'Delete', // cancelText: 'Cancel' // } // }); // dialogRef.afterClosed().subscribe(result => { // if (result) { // this.locationService.deleteLocation(locationId).subscribe({ // next: () => { // this.notificationService.showSuccess('Location deleted successfully'); // this.loadLocations(); // }, // error: (error) => { // let errorMessage = this.errorHandler.handleApiError(error, 'Failed to delete location'); // this.notificationService.showError(errorMessage); // console.error('Error deleting location:', error); // } // }); // } // }); // } getAddressLabel(address1: string, address2?: string): string { let addressLabel = address1; if (address2) { addressLabel += `, ${address2}`; } return addressLabel; } getCountryLabel(value: string): string { const country = this.countries.find(c => c.value === value); return country ? country.name : value; } cancelEdit(): void { this.showForm = false; this.isEditing = false; this.currentLocationId = null; this.locationForm.reset(); } }