diff --git a/src/app/app.routes.server.ts b/src/app/app.routes.server.ts index 69c62d8..5d09e56 100644 --- a/src/app/app.routes.server.ts +++ b/src/app/app.routes.server.ts @@ -9,6 +9,7 @@ export const serverRoutes: ServerRoute[] = [ { path: ':appId/edit-carnet/:headerid', renderMode: RenderMode.Client }, { path: ':appId/add-holder', renderMode: RenderMode.Client }, { path: ':appId/edit-holder/:holderid', renderMode: RenderMode.Client }, + { path: ':appId/terms', renderMode: RenderMode.Client }, { path: '**', renderMode: RenderMode.Prerender diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 22a957b..0948519 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -17,6 +17,7 @@ export const routes: Routes = [ { path: 'edit-carnet/:headerid', loadComponent: () => import('./carnet/edit/edit-carnet.component').then(m => m.EditCarnetComponent) }, { path: 'add-holder', loadComponent: () => import('./holder/add/add-holder.component').then(m => m.AddHolderComponent) }, { path: 'edit-holder/:holderid', loadComponent: () => import('./holder/edit/edit-holder.component').then(m => m.EditHolderComponent) }, + { path: 'terms', loadComponent: () => import('./carnet/terms-conditions/terms-conditions.component').then(m => m.TermsConditionsComponent) }, { path: '', redirectTo: 'home', pathMatch: 'full' } ], canActivate: [AuthGuard, AppIdGuard] diff --git a/src/app/carnet/add/add-carnet.component.html b/src/app/carnet/add/add-carnet.component.html index b12ae88..2e6da70 100644 --- a/src/app/carnet/add/add-carnet.component.html +++ b/src/app/carnet/add/add-carnet.component.html @@ -16,7 +16,7 @@ [editable]="stepsCompleted.applicationDetail"> Holder Selection + [applicationName]="applicationName" (updated)="onHolderSelectionUpdated($event)"> @@ -41,8 +41,14 @@ Shipping & Payment + + + + done + \ No newline at end of file diff --git a/src/app/carnet/add/add-carnet.component.ts b/src/app/carnet/add/add-carnet.component.ts index 6ffe92a..c092d38 100644 --- a/src/app/carnet/add/add-carnet.component.ts +++ b/src/app/carnet/add/add-carnet.component.ts @@ -25,8 +25,10 @@ export class AddCarnetComponent { applicationType: 'new' | 'additional' | 'duplicate' | 'extend' | null = 'new'; isEditMode = false; headerid: number = 0; + applicationName: string = ''; userPreferences: UserPreferences; refreshShippingData: boolean = false; + allSectionsCompleted: boolean = false; // Track completion of each step stepsCompleted = { @@ -47,12 +49,14 @@ export class AddCarnetComponent { onApplicationDetailCreated(data: { headerid: number, applicationName: string }): void { this.headerid = data.headerid; + this.applicationName = data.applicationName; this.stepsCompleted.applicationDetail = true; this.isLinear = false; // Disable linear mode after application detail is created } onHolderSelectionSaved(completed: boolean): void { this.stepsCompleted.holderSelection = completed; + this.isAllSectionsCompleted(); } onHolderSelectionUpdated(updated: boolean): void { @@ -61,13 +65,22 @@ export class AddCarnetComponent { onGoodsSectionSaved(completed: boolean): void { this.stepsCompleted.goodsSection = completed; + this.isAllSectionsCompleted(); } onTravelPlanSaved(completed: boolean): void { this.stepsCompleted.travelPlan = completed; + this.isAllSectionsCompleted(); } onShippingSaved(completed: boolean): void { this.stepsCompleted.shipping = completed; + this.isAllSectionsCompleted(); + } + + isAllSectionsCompleted(): void { + this.allSectionsCompleted = this.stepsCompleted.applicationDetail + && this.stepsCompleted.holderSelection && this.stepsCompleted.goodsSection + && this.stepsCompleted.shipping && this.stepsCompleted.travelPlan; } } diff --git a/src/app/carnet/edit/edit-carnet.component.html b/src/app/carnet/edit/edit-carnet.component.html index c3de58a..10901ec 100644 --- a/src/app/carnet/edit/edit-carnet.component.html +++ b/src/app/carnet/edit/edit-carnet.component.html @@ -41,8 +41,14 @@ Shipping & Payment + + + + done + \ No newline at end of file diff --git a/src/app/carnet/edit/edit-carnet.component.ts b/src/app/carnet/edit/edit-carnet.component.ts index fe1fc04..6cd0bf4 100644 --- a/src/app/carnet/edit/edit-carnet.component.ts +++ b/src/app/carnet/edit/edit-carnet.component.ts @@ -28,6 +28,7 @@ export class EditCarnetComponent { userPreferences: UserPreferences; applicationName: string = ''; refreshShippingData: boolean = false; + allSectionsCompleted: boolean = false; // Track completion of each step stepsCompleted = { @@ -54,6 +55,8 @@ export class EditCarnetComponent { if (returnTo === 'holder') { this.currentStep = 1; + } else if (returnTo === 'shipping') { + this.currentStep = 4; } } @@ -63,21 +66,32 @@ export class EditCarnetComponent { onHolderSelectionSaved(completed: boolean): void { this.stepsCompleted.holderSelection = completed; + this.isAllSectionsCompleted(); } onGoodsSectionSaved(completed: boolean): void { this.stepsCompleted.goodsSection = completed; + this.isAllSectionsCompleted(); } onHolderSelectionUpdated(updated: boolean): void { this.refreshShippingData = updated; + this.isAllSectionsCompleted(); } onTravelPlanSaved(completed: boolean): void { this.stepsCompleted.travelPlan = completed; + this.isAllSectionsCompleted(); } onShippingSaved(completed: boolean): void { this.stepsCompleted.shipping = completed; + this.isAllSectionsCompleted(); + } + + isAllSectionsCompleted() { + this.allSectionsCompleted = this.stepsCompleted.applicationDetail + && this.stepsCompleted.holderSelection && this.stepsCompleted.goodsSection + && this.stepsCompleted.shipping && this.stepsCompleted.travelPlan; } } diff --git a/src/app/carnet/goods/goods.component.ts b/src/app/carnet/goods/goods.component.ts index 4eeffda..7eff47c 100644 --- a/src/app/carnet/goods/goods.component.ts +++ b/src/app/carnet/goods/goods.component.ts @@ -102,6 +102,7 @@ export class GoodsComponent { next: (goodsDetail: Goods) => { if (goodsDetail) { this.patchFormData(goodsDetail); + this.completed.emit(this.goodsForm.valid && this.dataSource.data.length > 0); } this.isLoading = false; }, @@ -114,7 +115,6 @@ export class GoodsComponent { }); this.loadGoodsItems(); - this.completed.emit(this.goodsForm.valid && this.dataSource.data.length > 0); } } @@ -125,6 +125,7 @@ export class GoodsComponent { 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'); diff --git a/src/app/carnet/shipping/shipping.component.html b/src/app/carnet/shipping/shipping.component.html index bfdae98..edd359a 100644 --- a/src/app/carnet/shipping/shipping.component.html +++ b/src/app/carnet/shipping/shipping.component.html @@ -327,6 +327,11 @@
+ + + + + +
\ No newline at end of file diff --git a/src/app/carnet/terms-conditions/terms-conditions.component.scss b/src/app/carnet/terms-conditions/terms-conditions.component.scss new file mode 100644 index 0000000..8263cb2 --- /dev/null +++ b/src/app/carnet/terms-conditions/terms-conditions.component.scss @@ -0,0 +1,83 @@ +.terms-container { + display: flex; + justify-content: center; + align-items: center; +} + +.terms-card { + width: 100%; + max-height: 90vh; + display: flex; + flex-direction: column; +} + +mat-card-header { + justify-content: center; + padding: 16px 0; +} + +.terms-content { + flex: 1; + overflow-y: auto; + padding: 16px; + + h3 { + color: var(--mat-sys-primary); + margin-top: 24px; + margin-bottom: 12px; + } + + p { + padding-left: 16px; + } + + ul { + padding-left: 56px; + } + + li { + margin-bottom: 8px; + } + + strong { + font-weight: 500; + } +} + +.read-checkbox { + margin: 24px 0; + display: block; + padding: 8px 0; + border-top: 1px solid #eee; + border-bottom: 1px solid #eee; +} + +// Custom scrollbar +.terms-content::-webkit-scrollbar { + width: 8px; +} + +.terms-content::-webkit-scrollbar-track { + background: #f1f1f1; + border-radius: 4px; +} + +.terms-content::-webkit-scrollbar-thumb { + background: #888; + border-radius: 4px; +} + +.terms-content::-webkit-scrollbar-thumb:hover { + background: #555; +} + +// Responsive adjustments +@media (max-width: 768px) { + .terms-container { + padding: 12px; + } + + .terms-card { + max-height: 95vh; + } +} \ No newline at end of file diff --git a/src/app/carnet/terms-conditions/terms-conditions.component.ts b/src/app/carnet/terms-conditions/terms-conditions.component.ts new file mode 100644 index 0000000..16bfe08 --- /dev/null +++ b/src/app/carnet/terms-conditions/terms-conditions.component.ts @@ -0,0 +1,57 @@ + +import { Component, inject } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { ApiErrorHandlerService } from '../../core/services/common/api-error-handler.service'; +import { CommonService } from '../../core/services/common/common.service'; +import { NavigationService } from '../../core/services/common/navigation.service'; +import { NotificationService } from '../../core/services/common/notification.service'; +import { StorageService } from '../../core/services/common/storage.service'; + +@Component({ + selector: 'app-terms-conditions', + imports: [MatCardModule, + MatButtonModule, + MatDividerModule, + MatCheckboxModule, + CommonModule, + FormsModule], + templateUrl: './terms-conditions.component.html', + styleUrl: './terms-conditions.component.scss' +}) +export class TermsConditionsComponent { + + currentDate = new Date(); + hasReadAllTerms = false; + currentApplicationDetails: { headerid: number, applicationName: string } | null = null; + + private notificationService = inject(NotificationService); + private commonService = inject(CommonService); + private errorHandler = inject(ApiErrorHandlerService); + private storageService = inject(StorageService); + private navigationService = inject(NavigationService); + + constructor() { + this.currentApplicationDetails = this.storageService.get<{ headerid: number, applicationName: string }>('currentapplication') + } + + onAccept(): void { + console.log('Terms accepted'); + } + + onDecline(): void { + this.storageService.removeItem('currentapplication') + this.navigationService.navigate(["edit-carnet", this.currentApplicationDetails?.headerid], + { + state: { isEditMode: true }, + queryParams: { + applicationname: this.currentApplicationDetails?.applicationName, + return: 'shipping' + } + }) + } +} \ No newline at end of file diff --git a/src/app/carnet/travel-plan/travel-plan.component.html b/src/app/carnet/travel-plan/travel-plan.component.html index 43a2250..658f00a 100644 --- a/src/app/carnet/travel-plan/travel-plan.component.html +++ b/src/app/carnet/travel-plan/travel-plan.component.html @@ -1 +1,149 @@ -

travel-plan works!

+
+
+ +
+ + No of times entering & leaving USA + + + Required + + + Must be between 1-99 + + +
+ +
+ +
+

Visit Countries

+ + + + {{country.name}} + + +
+ + +
+ + No of times + + + Must be between 1-99 + + + +
+ + +
+
+ + +
+
+

Selected

+ + + {{country.name}} ({{country.visits}}) + + + No visit countries selected + + +
+
+
+ +
+ +
+

Transit Countries

+ + + {{country.name}} + + +
+ + +
+ + No of times + + + Must be between 1-99 + + + +
+ + +
+
+ + +
+
+

Selected

+ + + {{country.name}} ({{country.visits}}) + + + No transit countries selected + + +
+
+
+ +
+ +
+
+ Total Visits: + {{totalVisits}} +
+
+ Total Transits: + {{totalTransits}} +
+
+ + +
+ +
+
\ No newline at end of file diff --git a/src/app/carnet/travel-plan/travel-plan.component.scss b/src/app/carnet/travel-plan/travel-plan.component.scss index e69de29..7edc1cd 100644 --- a/src/app/carnet/travel-plan/travel-plan.component.scss +++ b/src/app/carnet/travel-plan/travel-plan.component.scss @@ -0,0 +1,127 @@ +.travel-plan-container { + padding: 8px; + // max-width: 1200px; + margin: 0 auto; + + // .form-row { + // margin-bottom: 24px; + // max-width: 300px; + // } + + .usa-entries { + width: 300px; + } + + .country-selection-container { + display: flex; + gap: 24px; + margin-bottom: 12px; + + @media (max-width: 768px) { + flex-direction: column; + } + } + + .available-countries, + .selected-countries { + flex: 1; + border: 1px solid #e0e0e0; + border-radius: 4px; + padding: 16px; + overflow: hidden; + + h3 { + margin: 0 0 16px 0; + font-size: 16px; + font-weight: 500; + } + + mat-selection-list { + height: 150px; + overflow-y: auto; + padding: 0; + + mat-list-item { + .empty-message { + font-style: italic; + } + } + } + } + + .controls { + display: flex; + flex-direction: column; + gap: 16px; + width: 150px; + padding: 16px; + + .action-buttons { + display: flex; + flex-direction: column; + gap: 8px; + + button { + width: 100%; + display: flex; + // gap: 8px; + align-items: center; + } + + mat-icon { + margin: 0; + } + + button.icon-end { + flex-direction: row-reverse; + } + } + } + + .selected-countries { + display: flex; + flex-direction: column; + gap: 16px; + + .visit-countries, + .transit-countries { + flex: 1; + } + } + + .form-actions { + display: flex; + justify-content: flex-end; + padding-top: 12px; + border-top: 1px solid #e0e0e0; + justify-content: space-between; + + .totals-section { + display: flex; + gap: 24px; + + .total-item { + display: flex; + gap: 8px; + align-items: center; + + .total-value { + font-weight: 500; + } + } + } + } +} + +@media (max-width: 768px) { + .travel-container { + .form-row { + flex-direction: column; + gap: 8px; + + mat-form-field { + width: 100%; + } + } + } +} \ No newline at end of file diff --git a/src/app/carnet/travel-plan/travel-plan.component.ts b/src/app/carnet/travel-plan/travel-plan.component.ts index 6eeda3a..57b3f8e 100644 --- a/src/app/carnet/travel-plan/travel-plan.component.ts +++ b/src/app/carnet/travel-plan/travel-plan.component.ts @@ -1,12 +1,284 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Component, EventEmitter, inject, Input, Output } from '@angular/core'; +import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; +import { 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: [], + imports: [AngularMaterialModule, ReactiveFormsModule, CommonModule, MatListModule], templateUrl: './travel-plan.component.html', styleUrl: './travel-plan.component.scss' }) export class TravelPlanComponent { @Input() headerid: number = 0; @Output() completed = 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; + 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(); + } + } + + 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 + }); + } + } + + 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 { + 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.addVisitCountryToCollection(); + } + }); + } + + // 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 + }); + } + } + + // Remove country from visits + removeVisitCountry(): void { + if (!this.selectedVisitCountry) return; + + this.selectedVisitCountries = this.selectedVisitCountries.filter( + c => c.value !== this.selectedVisitCountry!.value + ); + } + + // 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) + }).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; + } + }); + } + + private patchTravelPlanData(plan: TravelPlan): void { + + this.selectedVisitCountries = plan?.entries; + this.selectedTransitCountries = plan?.transits; + + // Patch form values + this.travelForm.patchValue({ + usaEntries: plan?.usSets, + visitsInput: 1, + transitsInput: 1 // Reset to default + }); + + this.completed.emit(this.travelForm.valid); + } + + onSubmit(): void { + if (this.travelForm.invalid) { + this.travelForm.markAllAsTouched(); + return; + } + + const travelPlan: TravelPlan = { + usSets: this.travelForm.value.usaEntries, + entries: this.selectedVisitCountries, + transits: this.selectedTransitCountries + }; + + this.isLoading = true; + this.travelPlanService.saveTravelPlan(this.headerid, travelPlan).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/core/models/carnet/travel-plan.ts b/src/app/core/models/carnet/travel-plan.ts index 34d68ea..d2cb9f7 100644 --- a/src/app/core/models/carnet/travel-plan.ts +++ b/src/app/core/models/carnet/travel-plan.ts @@ -1,2 +1,17 @@ export interface TravelPlan { + usSets?: number; + entries: TravelEntry[]; + transits: TravelEntry[]; +} + +export interface Country { + name?: string; + value: string; + actionMessage?: string | null; + actionType?: string | null; + action?: string | null; +} + +export interface TravelEntry extends Country { + visits: number; } diff --git a/src/app/core/services/carnet/application-detail.service.ts b/src/app/core/services/carnet/application-detail.service.ts index a4fd77b..0190980 100644 --- a/src/app/core/services/carnet/application-detail.service.ts +++ b/src/app/core/services/carnet/application-detail.service.ts @@ -3,7 +3,7 @@ import { environment } from '../../../../environments/environment'; import { HttpClient } from '@angular/common/http'; import { UserService } from '../common/user.service'; import { ApplicationDetail } from '../../models/carnet/application-detail'; -import { filter, map, Observable } from 'rxjs'; +import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root' @@ -15,21 +15,6 @@ export class ApplicationDetailService { private http = inject(HttpClient); private userService = inject(UserService); - // getApplicationDetailsById(id: number): ApplicationDetail | any { - // return this.http.get(`${this.apiUrl}/${this.apiDb}/GetPreparerByClientid/${this.userService.getUserSpid()}/${id}`).pipe( - // filter(response => response.length > 0), - // map(response => this.mapToApplicationDetail(response?.[0]))); - // } - - // private mapToApplicationDetail(applicationDetails: any): ApplicationDetail { - // return { - // clientid: applicationDetails.CLIENTID, - // spid: applicationDetails.SPID, - // name: applicationDetails.P_APPLICATIONNAME, - // applicationId: applicationDetails.P_HEADERID - // }; - // } - createApplicationDetails(data: ApplicationDetail): Observable { const userDetails = this.userService.getUserDetails(); diff --git a/src/app/core/services/carnet/goods.service.ts b/src/app/core/services/carnet/goods.service.ts index dd18452..ec9d430 100644 --- a/src/app/core/services/carnet/goods.service.ts +++ b/src/app/core/services/carnet/goods.service.ts @@ -3,7 +3,7 @@ import { environment } from '../../../../environments/environment'; import { HttpClient } from '@angular/common/http'; import { UserService } from '../common/user.service'; import { Goods, GoodsItem } from '../../models/carnet/goods'; -import { filter, map, Observable } from 'rxjs'; +import { map, Observable } from 'rxjs'; @Injectable({ providedIn: 'root' @@ -17,8 +17,7 @@ export class GoodsService { getGoodDetailsByHeaderId(headerid: number): Goods | any { return this.http.get(`${this.apiUrl}/${this.apiDb}/GetGoodsDetailstoEdit/${this.userService.getUserSpid()}/${this.userService.getUser()}/${headerid}`).pipe( - filter(response => response.length > 0), - map(response => this.mapToGoods(response?.[0]))); + map(response => this.mapToGoods(response))); } private mapToGoods(goodDetails: any): Goods { diff --git a/src/app/core/services/carnet/holder.service.ts b/src/app/core/services/carnet/holder.service.ts index 40e25ca..9cb3090 100644 --- a/src/app/core/services/carnet/holder.service.ts +++ b/src/app/core/services/carnet/holder.service.ts @@ -3,7 +3,7 @@ import { environment } from '../../../../environments/environment'; import { HttpClient } from '@angular/common/http'; import { UserService } from '../common/user.service'; import { Holder } from '../../models/carnet/holder'; -import { filter, map, Observable } from 'rxjs'; +import { map, Observable } from 'rxjs'; @Injectable({ providedIn: 'root' @@ -17,8 +17,7 @@ export class HolderService { getHolder(id: number): Observable { return this.http.get(`${this.apiUrl}/${this.apiDb}/GetHolderstoEdit/${this.userService.getUserSpid()}/${this.userService.getUser()}/${id}`).pipe( - filter(response => response.length > 0), - map(response => this.mapToHolder(response?.[0]))); + map(response => this.mapToHolder(response))); } private mapToHolder(holder: any): Holder { diff --git a/src/app/core/services/carnet/shipping.service.ts b/src/app/core/services/carnet/shipping.service.ts index 5dc7760..5eddc2c 100644 --- a/src/app/core/services/carnet/shipping.service.ts +++ b/src/app/core/services/carnet/shipping.service.ts @@ -19,8 +19,7 @@ export class ShippingService { getShippingData(headerid: number): Shipping | any { return this.http.get(`${this.apiUrl}/${this.apiDb}/GetShipPaymentDetailstoEdit/${this.userService.getUserSpid()}/${this.userService.getUser()}/${headerid}`).pipe( - filter(response => response.length > 0), - map(response => this.mapToShippingData(response?.[0]))); + map(response => this.mapToShippingData(response))); } private mapToShippingData(shippingDetails: any): Shipping { diff --git a/src/app/core/services/carnet/travel-plan.service.ts b/src/app/core/services/carnet/travel-plan.service.ts index 498f0f4..13d42da 100644 --- a/src/app/core/services/carnet/travel-plan.service.ts +++ b/src/app/core/services/carnet/travel-plan.service.ts @@ -1,9 +1,93 @@ -import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { inject, Injectable } from '@angular/core'; +import { Observable, map } from 'rxjs'; +import { environment } from '../../../../environments/environment'; +import { Country, TravelPlan } from '../../models/carnet/travel-plan'; +import { UserService } from '../common/user.service'; @Injectable({ providedIn: 'root' }) export class TravelPlanService { + private apiUrl = environment.apiUrl; + private apiDb = environment.apiDb; - constructor() { } + private http = inject(HttpClient); + private userService = inject(UserService); + + getCountriesAndMessages(): Observable { + return this.http.get(`${this.apiUrl}/${this.apiDb}/GetCountriesAndMessages`).pipe( + map((response) => + response.map((item) => ({ + name: item.COUNTRYNAME, + value: item.COUNTRYCODE, + actionMessage: item.COUNTRYMESSAGE, + actionType: item.ADDLPARAMVALUE1, + action: item.ADDLPARAMVALUE2 + })) + ) + ); + } + + getTravelPlan(headerid: number): Observable { + return this.http.get(`${this.apiUrl}/${this.apiDb}/GetCountryDetailstoEdit/${this.userService.getUserSpid()}/${this.userService.getUser()}/${headerid}`).pipe( + map(response => this.mapToTravelPlanData(response))); + } + + private mapToTravelPlanData(travelPlanDetails: any): TravelPlan { + + if (!travelPlanDetails?.P_COUNTRYTABLE && !travelPlanDetails?.NOOFUSSETS) { + return { + entries: [], + transits: [] + } as TravelPlan; + } + + const countryTable: any[] = travelPlanDetails.P_COUNTRYTABLE; + + let travelPlanData: TravelPlan = { + usSets: travelPlanDetails.NOOFUSSETS, + entries: countryTable + .filter((item) => item.VISISTTRANSITIND === 'V') + .map(item => ({ + name: item.COUNTRYNAME, + value: item.COUNTRYCODE, + visits: item.NOOFTIMESENTLEAVE + })), + transits: countryTable + .filter(item => item.VISISTTRANSITIND === 'T') + .map(item => ({ + name: item.COUNTRYNAME, + value: item.COUNTRYCODE, + visits: item.NOOFTIMESENTLEAVE + })) + }; + + return travelPlanData; + } + + saveTravelPlan(headerid: number, travelPlan: TravelPlan): Observable { + + const countryTable: any[] = [ + ...travelPlan.entries.map(entry => ({ + P_VISISTTRANSITIND: 'V' as const, + COUNTRYCODE: entry.value, + NOOFTIMESENTLEAVE: entry.visits + })), + ...travelPlan.transits.map(transit => ({ + P_VISISTTRANSITIND: 'T' as const, + COUNTRYCODE: transit.value, + NOOFTIMESENTLEAVE: transit.visits + })) + ]; + + let travelPlanData = { + P_HEADERID: headerid, + P_USSETS: travelPlan.usSets, + P_COUNTRYTABLE: countryTable, + P_USERID: this.userService.getUser() + } + + return this.http.post(`${this.apiUrl}/${this.apiDb}/AddCountries`, travelPlanData); + } }