extend carnet and feedback updates
This commit is contained in:
parent
420900fec1
commit
0cd13190b6
@ -8,6 +8,7 @@ export const serverRoutes: ServerRoute[] = [
|
|||||||
{ path: ':appId/add-carnet', renderMode: RenderMode.Client },
|
{ path: ':appId/add-carnet', renderMode: RenderMode.Client },
|
||||||
{ path: ':appId/edit-carnet/:headerid', renderMode: RenderMode.Client },
|
{ path: ':appId/edit-carnet/:headerid', renderMode: RenderMode.Client },
|
||||||
{ path: ':appId/view-carnet/:headerid', renderMode: RenderMode.Client },
|
{ path: ':appId/view-carnet/:headerid', renderMode: RenderMode.Client },
|
||||||
|
{ path: ':appId/view-extend-carnet/:headerid', renderMode: RenderMode.Client },
|
||||||
{ path: ':appId/duplicate-carnet/:headerid', renderMode: RenderMode.Client },
|
{ path: ':appId/duplicate-carnet/:headerid', renderMode: RenderMode.Client },
|
||||||
{ path: ':appId/extend-carnet/:headerid', renderMode: RenderMode.Client },
|
{ path: ':appId/extend-carnet/:headerid', renderMode: RenderMode.Client },
|
||||||
{ path: ':appId/additional-sets/:headerid', renderMode: RenderMode.Client },
|
{ path: ':appId/additional-sets/:headerid', renderMode: RenderMode.Client },
|
||||||
|
|||||||
@ -18,6 +18,7 @@ export const routes: Routes = [
|
|||||||
{ path: 'add-holder', loadComponent: () => import('./holder/add/add-holder.component').then(m => m.AddHolderComponent) },
|
{ 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: 'edit-holder/:holderid', loadComponent: () => import('./holder/edit/edit-holder.component').then(m => m.EditHolderComponent) },
|
||||||
{ path: 'view-carnet/:headerid', loadComponent: () => import('./carnet/view/view-carnet.component').then(m => m.ViewCarnetComponent) },
|
{ path: 'view-carnet/:headerid', loadComponent: () => import('./carnet/view/view-carnet.component').then(m => m.ViewCarnetComponent) },
|
||||||
|
{ path: 'view-extend-carnet/:headerid', loadComponent: () => import('./carnet/view/view-carnet.component').then(m => m.ViewCarnetComponent) },
|
||||||
{ path: 'duplicate-carnet/:headerid', loadComponent: () => import('./carnet/edit/edit-carnet.component').then(m => m.EditCarnetComponent) },
|
{ path: 'duplicate-carnet/:headerid', loadComponent: () => import('./carnet/edit/edit-carnet.component').then(m => m.EditCarnetComponent) },
|
||||||
{ path: 'extend-carnet/:headerid', loadComponent: () => import('./carnet/edit/edit-carnet.component').then(m => m.EditCarnetComponent) },
|
{ path: 'extend-carnet/:headerid', loadComponent: () => import('./carnet/edit/edit-carnet.component').then(m => m.EditCarnetComponent) },
|
||||||
{ path: 'additional-sets/:headerid', loadComponent: () => import('./carnet/edit/edit-carnet.component').then(m => m.EditCarnetComponent) },
|
{ path: 'additional-sets/:headerid', loadComponent: () => import('./carnet/edit/edit-carnet.component').then(m => m.EditCarnetComponent) },
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
<p>additional-set works!</p>
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-additional-set',
|
|
||||||
imports: [],
|
|
||||||
templateUrl: './additional-set.component.html',
|
|
||||||
styleUrl: './additional-set.component.scss'
|
|
||||||
})
|
|
||||||
export class AdditionalSetComponent {
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1 +0,0 @@
|
|||||||
<p>duplicate works!</p>
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-duplicate',
|
|
||||||
imports: [],
|
|
||||||
templateUrl: './duplicate.component.html',
|
|
||||||
styleUrl: './duplicate.component.scss'
|
|
||||||
})
|
|
||||||
export class DuplicateComponent {
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -3,6 +3,15 @@
|
|||||||
<mat-stepper orientation="vertical" [linear]="isLinear" (selectionChange)="onStepChange($event)"
|
<mat-stepper orientation="vertical" [linear]="isLinear" (selectionChange)="onStepChange($event)"
|
||||||
[selectedIndex]="currentStep">
|
[selectedIndex]="currentStep">
|
||||||
|
|
||||||
|
<!-- Replacement Application Step -->
|
||||||
|
<mat-step *ngIf="applicationType === 'extend'" [completed]="stepsCompleted.extension"
|
||||||
|
[editable]="stepsCompleted.extension">
|
||||||
|
<ng-template matStepLabel>Extension Application</ng-template>
|
||||||
|
<app-replacement-set (completed)="onExtensionApplicationSaved($event)" [headerid]="headerid"
|
||||||
|
[isEditMode]="isEditMode && applicationType === 'extend'">
|
||||||
|
</app-replacement-set>
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
<!-- Application Name Step -->
|
<!-- Application Name Step -->
|
||||||
<mat-step [completed]="stepsCompleted.applicationDetail" [editable]="stepsCompleted.applicationDetail">
|
<mat-step [completed]="stepsCompleted.applicationDetail" [editable]="stepsCompleted.applicationDetail">
|
||||||
<ng-template matStepLabel>Application Name</ng-template>
|
<ng-template matStepLabel>Application Name</ng-template>
|
||||||
|
|||||||
@ -11,11 +11,12 @@ import { ShippingComponent } from '../shipping/shipping.component';
|
|||||||
import { TravelPlanComponent } from '../travel-plan/travel-plan.component';
|
import { TravelPlanComponent } from '../travel-plan/travel-plan.component';
|
||||||
import { UserPreferences } from '../../core/models/user-preference';
|
import { UserPreferences } from '../../core/models/user-preference';
|
||||||
import { UserPreferencesService } from '../../core/services/user-preference.service';
|
import { UserPreferencesService } from '../../core/services/user-preference.service';
|
||||||
|
import { ReplacementSetComponent } from '../replacement-set/replacement-set.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-edit-carnet',
|
selector: 'app-edit-carnet',
|
||||||
imports: [AngularMaterialModule, CommonModule, ReactiveFormsModule,
|
imports: [AngularMaterialModule, CommonModule, ReactiveFormsModule,
|
||||||
ApplicationComponent, HolderComponent, GoodsComponent, TravelPlanComponent, ShippingComponent],
|
ApplicationComponent, HolderComponent, GoodsComponent, TravelPlanComponent, ShippingComponent, ReplacementSetComponent],
|
||||||
templateUrl: './edit-carnet.component.html',
|
templateUrl: './edit-carnet.component.html',
|
||||||
styleUrl: './edit-carnet.component.scss'
|
styleUrl: './edit-carnet.component.scss'
|
||||||
})
|
})
|
||||||
@ -38,7 +39,8 @@ export class EditCarnetComponent {
|
|||||||
holderSelection: false,
|
holderSelection: false,
|
||||||
goodsSection: false,
|
goodsSection: false,
|
||||||
travelPlan: false,
|
travelPlan: false,
|
||||||
shipping: false
|
shipping: false,
|
||||||
|
extension: false
|
||||||
};
|
};
|
||||||
|
|
||||||
private route = inject(ActivatedRoute);
|
private route = inject(ActivatedRoute);
|
||||||
@ -70,6 +72,8 @@ export class EditCarnetComponent {
|
|||||||
|
|
||||||
if (returnTo === 'holder') {
|
if (returnTo === 'holder') {
|
||||||
this.currentStep = 1;
|
this.currentStep = 1;
|
||||||
|
} else if (returnTo === 'shipping' && this.applicationType === 'extend') {
|
||||||
|
this.currentStep = 5;
|
||||||
} else if (returnTo === 'shipping') {
|
} else if (returnTo === 'shipping') {
|
||||||
this.currentStep = 4;
|
this.currentStep = 4;
|
||||||
} else if (this.applicationType === 'additional') {
|
} else if (this.applicationType === 'additional') {
|
||||||
@ -115,9 +119,15 @@ export class EditCarnetComponent {
|
|||||||
this.shippingComponent?.refreshInsuranceData(data);
|
this.shippingComponent?.refreshInsuranceData(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onExtensionApplicationSaved(completed: boolean): void {
|
||||||
|
this.stepsCompleted.extension = completed;
|
||||||
|
this.isAllSectionsCompleted();
|
||||||
|
}
|
||||||
|
|
||||||
isAllSectionsCompleted() {
|
isAllSectionsCompleted() {
|
||||||
this.allSectionsCompleted = this.stepsCompleted.applicationDetail
|
this.allSectionsCompleted = this.stepsCompleted.applicationDetail
|
||||||
&& this.stepsCompleted.holderSelection && this.stepsCompleted.goodsSection
|
&& this.stepsCompleted.holderSelection && this.stepsCompleted.goodsSection
|
||||||
&& this.stepsCompleted.shipping && this.stepsCompleted.travelPlan;
|
&& this.stepsCompleted.shipping && this.stepsCompleted.travelPlan
|
||||||
|
&& (this.applicationType !== 'extend' || this.stepsCompleted.extension);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,8 +33,15 @@ export class GoodsComponent {
|
|||||||
@Input() isEditMode: boolean = false;
|
@Input() isEditMode: boolean = false;
|
||||||
@Output() completed = new EventEmitter<boolean>();
|
@Output() completed = new EventEmitter<boolean>();
|
||||||
|
|
||||||
@ViewChild(MatPaginator) paginator!: MatPaginator;
|
@ViewChild(MatPaginator, { static: false })
|
||||||
@ViewChild(MatSort) sort!: MatSort;
|
set paginator(value: MatPaginator) {
|
||||||
|
this.dataSource.paginator = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewChild(MatSort, { static: false })
|
||||||
|
set sort(value: MatSort) {
|
||||||
|
this.dataSource.sort = value;
|
||||||
|
}
|
||||||
|
|
||||||
// Table configuration
|
// Table configuration
|
||||||
displayedColumns: string[] = ['itemNumber', 'description', 'pieces', 'weight', 'unitOfMeasure', 'value', 'countryOfOrigin', 'actions'];
|
displayedColumns: string[] = ['itemNumber', 'description', 'pieces', 'weight', 'unitOfMeasure', 'value', 'countryOfOrigin', 'actions'];
|
||||||
|
|||||||
@ -1 +1,98 @@
|
|||||||
<p>replacement-set works!</p>
|
<!-- extension-section.component.html -->
|
||||||
|
<div class="extension-container">
|
||||||
|
<div class="loading-shade" *ngIf="isLoading">
|
||||||
|
<mat-spinner diameter="50"></mat-spinner>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- General Section -->
|
||||||
|
<div class="general-section">
|
||||||
|
<h3>General</h3>
|
||||||
|
<div class="detail-row">
|
||||||
|
<label>Carnet No:</label>
|
||||||
|
<span>{{ replacementDetails?.replacementCarnetNumber || 'N/A' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="detail-row">
|
||||||
|
<label>Issue Date:</label>
|
||||||
|
<span>{{ replacementDetails?.issueDate | date:'mediumDate':'UTC' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="detail-row">
|
||||||
|
<label>Preparer Name:</label>
|
||||||
|
<span>{{ replacementDetails?.preparerName || 'N/A' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="detail-row">
|
||||||
|
<label>Address:</label>
|
||||||
|
<span>{{ getAddressLabel() || 'N/A' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Extension Section -->
|
||||||
|
<div class="extension-section">
|
||||||
|
<h3>Extension</h3>
|
||||||
|
|
||||||
|
<form [formGroup]="extensionForm" (ngSubmit)="onSubmit()">
|
||||||
|
<div class="form-row">
|
||||||
|
<mat-form-field appearance="outline" class="current-port">
|
||||||
|
<mat-label>Current port where the goods are located</mat-label>
|
||||||
|
<input matInput formControlName="portName" required>
|
||||||
|
<mat-error *ngIf="f['portName'].errors?.['required']">
|
||||||
|
Port location is required
|
||||||
|
</mat-error>
|
||||||
|
<mat-error *ngIf="f['portName'].errors?.['maxlength']">
|
||||||
|
Maximum 100 characters allowed
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="country">
|
||||||
|
<mat-label>Country</mat-label>
|
||||||
|
<mat-select formControlName="portCountry" required>
|
||||||
|
<mat-option *ngFor="let country of countries" [value]="country.value">
|
||||||
|
{{ country.name }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error *ngIf="f['portCountry'].errors?.['required']">
|
||||||
|
Port Country is required
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<mat-form-field appearance="outline" class="reason">
|
||||||
|
<mat-label>Reason for Extension</mat-label>
|
||||||
|
<mat-select formControlName="reasonForExtension" required>
|
||||||
|
<mat-option *ngFor="let reason of extensionReasons" [value]="reason.value">
|
||||||
|
{{ reason.name }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error *ngIf="f['reasonForExtension'].errors?.['required']">
|
||||||
|
Reason is required
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="duration">
|
||||||
|
<mat-label>Duration</mat-label>
|
||||||
|
<input matInput type="number" formControlName="duration" required>
|
||||||
|
<span matTextSuffix>Month(s)</span>
|
||||||
|
<mat-error *ngIf="f['duration'].errors?.['required']">
|
||||||
|
Duration is required
|
||||||
|
</mat-error>
|
||||||
|
<mat-error *ngIf="f['duration'].errors?.['min']">
|
||||||
|
Minimum 1 month required
|
||||||
|
</mat-error>
|
||||||
|
<mat-error *ngIf="f['duration'].errors?.['max']">
|
||||||
|
Maximum 24 months allowed
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-actions">
|
||||||
|
<button mat-raised-button color="primary" type="submit" *ngIf="!isViewMode"
|
||||||
|
[disabled]="extensionForm.invalid || changeInProgress">
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
.extension-container {
|
||||||
|
padding: 0.5rem;
|
||||||
|
|
||||||
|
.general-section,
|
||||||
|
.extension-section {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
color: var(--mat-sys-primary);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-row {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: inline-block;
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
align-items: start;
|
||||||
|
|
||||||
|
mat-form-field {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-port {
|
||||||
|
grid-column: span 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,11 +1,173 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component, EventEmitter, inject, Input, Output } from '@angular/core';
|
||||||
|
import { FormGroup, FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { finalize, Subject, takeUntil } from 'rxjs';
|
||||||
|
import { Country } from '../../core/models/country';
|
||||||
|
import { CommonService } from '../../core/services/common/common.service';
|
||||||
|
import { Replacement } from '../../core/models/carnet/replacement';
|
||||||
|
import { ReplacementService } from '../../core/services/carnet/replacement.service';
|
||||||
|
import { ApiErrorHandlerService } from '../../core/services/common/api-error-handler.service';
|
||||||
|
import { NotificationService } from '../../core/services/common/notification.service';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { AngularMaterialModule } from '../../shared/module/angular-material.module';
|
||||||
|
import { ExtensionReason } from '../../core/models/extension-reason';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-replacement-set',
|
selector: 'app-replacement-set',
|
||||||
imports: [],
|
imports: [AngularMaterialModule, CommonModule, ReactiveFormsModule],
|
||||||
templateUrl: './replacement-set.component.html',
|
templateUrl: './replacement-set.component.html',
|
||||||
styleUrl: './replacement-set.component.scss'
|
styleUrl: './replacement-set.component.scss'
|
||||||
})
|
})
|
||||||
export class ReplacementSetComponent {
|
export class ReplacementSetComponent {
|
||||||
|
@Input() headerid: number = 0;
|
||||||
|
@Input() isViewMode = false;
|
||||||
|
@Input() isEditMode: boolean = false;
|
||||||
|
@Output() completed = new EventEmitter<boolean>();
|
||||||
|
|
||||||
}
|
extensionForm: FormGroup;
|
||||||
|
countries: Country[] = [];
|
||||||
|
extensionReasons: ExtensionReason[] = [];
|
||||||
|
replacementDetails: Replacement | undefined;
|
||||||
|
|
||||||
|
// UI state
|
||||||
|
isEditing = false;
|
||||||
|
isLoading = false;
|
||||||
|
changeInProgress = false;
|
||||||
|
|
||||||
|
private destroy$ = new Subject<void>();
|
||||||
|
|
||||||
|
private fb = inject(FormBuilder);
|
||||||
|
private commonService = inject(CommonService);
|
||||||
|
private notificationService = inject(NotificationService);
|
||||||
|
private replacementService = inject(ReplacementService);
|
||||||
|
private errorHandler = inject(ApiErrorHandlerService);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
) {
|
||||||
|
this.extensionForm = this.createForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.loadCountries();
|
||||||
|
this.loadExtensionReasons();
|
||||||
|
|
||||||
|
if (this.headerid > 0) {
|
||||||
|
this.isLoading = true;
|
||||||
|
this.replacementService.getExtensionData(this.headerid).pipe(finalize(() => {
|
||||||
|
this.isLoading = false;
|
||||||
|
})).subscribe({
|
||||||
|
next: (replacementData: Replacement) => {
|
||||||
|
if (replacementData) {
|
||||||
|
this.patchFormData(replacementData);
|
||||||
|
this.replacementDetails = replacementData;
|
||||||
|
this.completed.emit(this.extensionForm.valid || this.extensionForm.disabled);
|
||||||
|
this.completed.emit(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: (error: any) => {
|
||||||
|
let errorMessage = this.errorHandler.handleApiError(error, 'Failed to load extension details');
|
||||||
|
this.notificationService.showError(errorMessage);
|
||||||
|
console.error('Error loading extension details:', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.destroy$.next();
|
||||||
|
this.destroy$.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
patchFormData(data: Replacement): void {
|
||||||
|
this.extensionForm.patchValue({
|
||||||
|
portName: data.portName,
|
||||||
|
portCountry: data.portCountry,
|
||||||
|
reasonForExtension: data.reasonForExtension,
|
||||||
|
duration: data.duration
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.isViewMode) {
|
||||||
|
this.extensionForm.disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createForm(): FormGroup {
|
||||||
|
return this.fb.group({
|
||||||
|
portName: ['', [Validators.required, Validators.maxLength(100)]],
|
||||||
|
portCountry: ['', Validators.required],
|
||||||
|
reasonForExtension: ['', Validators.required],
|
||||||
|
duration: [1, [Validators.required, Validators.min(1), Validators.max(24)]]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
loadCountries(): void {
|
||||||
|
this.commonService.getCountries()
|
||||||
|
.pipe(takeUntil(this.destroy$))
|
||||||
|
.subscribe({
|
||||||
|
next: (countries) => {
|
||||||
|
this.countries = countries;
|
||||||
|
},
|
||||||
|
error: (error) => {
|
||||||
|
console.error('Failed to load countries', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
loadExtensionReasons(): void {
|
||||||
|
this.commonService.getExtensionReasons()
|
||||||
|
.pipe(takeUntil(this.destroy$))
|
||||||
|
.subscribe({
|
||||||
|
next: (extensionReasons) => {
|
||||||
|
this.extensionReasons = extensionReasons;
|
||||||
|
},
|
||||||
|
error: (error) => {
|
||||||
|
console.error('Failed to load extension reasons', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmit(): void {
|
||||||
|
if (this.extensionForm.invalid) {
|
||||||
|
this.extensionForm.markAllAsTouched();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = this.extensionForm.value;
|
||||||
|
this.changeInProgress = true;
|
||||||
|
|
||||||
|
this.replacementService.saveExtensionDetails(this.headerid, formData).pipe(finalize(() => {
|
||||||
|
this.changeInProgress = false;
|
||||||
|
})).subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.notificationService.showSuccess('Extension details saved successfully');
|
||||||
|
this.completed.emit(this.extensionForm.valid);
|
||||||
|
},
|
||||||
|
error: (error) => {
|
||||||
|
let errorMessage = this.errorHandler.handleApiError(error, 'Failed to save extension information');
|
||||||
|
this.notificationService.showError(errorMessage);
|
||||||
|
console.error('Error saving extension information:', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get f() {
|
||||||
|
return this.extensionForm.controls;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAddressLabel(): string {
|
||||||
|
if (!this.replacementDetails) {
|
||||||
|
return 'No contact information available';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build name parts, filtering out null/undefined/empty strings
|
||||||
|
const allParts = [
|
||||||
|
this.replacementDetails.address1,
|
||||||
|
this.replacementDetails.address2,
|
||||||
|
this.replacementDetails.city,
|
||||||
|
this.replacementDetails.state,
|
||||||
|
this.replacementDetails.country,
|
||||||
|
this.replacementDetails.zip
|
||||||
|
].filter(part => part != null && part.trim() !== '');
|
||||||
|
|
||||||
|
return allParts.join(', ') || '';
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -180,6 +180,11 @@ export class ShippingComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.applicationType === 'extend' && this.shippingFromDB) {
|
||||||
|
shippingData.needsLostDocProtection = this.shippingFromDB.needsLostDocProtection;
|
||||||
|
shippingData.formOfSecurity = this.shippingFromDB.formOfSecurity;
|
||||||
|
}
|
||||||
|
|
||||||
this.shippingService.saveShippingDetails(this.headerid, shippingData).pipe(finalize(() => {
|
this.shippingService.saveShippingDetails(this.headerid, shippingData).pipe(finalize(() => {
|
||||||
this.changeInProgress = false;
|
this.changeInProgress = false;
|
||||||
})).subscribe({
|
})).subscribe({
|
||||||
@ -483,9 +488,13 @@ export class ShippingComponent {
|
|||||||
this.shippingForm.disable();
|
this.shippingForm.disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.applicationType === 'duplicate' || this.applicationType === 'additional') {
|
if (this.applicationType === 'duplicate' || this.applicationType === 'additional'
|
||||||
|
|| this.applicationType === 'extend') {
|
||||||
this.shippingForm.get('formOfSecurity')?.disable();
|
this.shippingForm.get('formOfSecurity')?.disable();
|
||||||
this.shippingForm.get('needsLostDocProtection')?.disable();
|
this.shippingForm.get('needsLostDocProtection')?.disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.applicationType === 'duplicate' || this.applicationType === 'additional') {
|
||||||
this.shippingForm.get('needsInsurance')?.disable();
|
this.shippingForm.get('needsInsurance')?.disable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,15 @@
|
|||||||
<button mat-button (click)="accordion().closeAll()">Collapse All</button>
|
<button mat-button (click)="accordion().closeAll()">Collapse All</button>
|
||||||
</div>
|
</div>
|
||||||
<mat-accordion class="carnet-headers-align" multi>
|
<mat-accordion class="carnet-headers-align" multi>
|
||||||
|
|
||||||
|
<mat-expansion-panel *ngIf="applicationType==='extend'">
|
||||||
|
<mat-expansion-panel-header>
|
||||||
|
<mat-panel-title> Extension Application </mat-panel-title>
|
||||||
|
</mat-expansion-panel-header>
|
||||||
|
<app-replacement-set [headerid]=" headerid" [isViewMode]="isViewMode">
|
||||||
|
</app-replacement-set>
|
||||||
|
</mat-expansion-panel>
|
||||||
|
|
||||||
<mat-expansion-panel>
|
<mat-expansion-panel>
|
||||||
<mat-expansion-panel-header>
|
<mat-expansion-panel-header>
|
||||||
<mat-panel-title> Application Name </mat-panel-title>
|
<mat-panel-title> Application Name </mat-panel-title>
|
||||||
|
|||||||
@ -11,11 +11,12 @@ import { GoodsComponent } from '../goods/goods.component';
|
|||||||
import { HolderComponent } from '../holder/holder.component';
|
import { HolderComponent } from '../holder/holder.component';
|
||||||
import { ShippingComponent } from '../shipping/shipping.component';
|
import { ShippingComponent } from '../shipping/shipping.component';
|
||||||
import { TravelPlanComponent } from '../travel-plan/travel-plan.component';
|
import { TravelPlanComponent } from '../travel-plan/travel-plan.component';
|
||||||
|
import { ReplacementSetComponent } from '../replacement-set/replacement-set.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-view-carnet',
|
selector: 'app-view-carnet',
|
||||||
imports: [AngularMaterialModule, CommonModule, ReactiveFormsModule,
|
imports: [AngularMaterialModule, CommonModule, ReactiveFormsModule,
|
||||||
ApplicationComponent, HolderComponent, GoodsComponent, TravelPlanComponent, ShippingComponent],
|
ApplicationComponent, HolderComponent, GoodsComponent, TravelPlanComponent, ShippingComponent, ReplacementSetComponent],
|
||||||
templateUrl: './view-carnet.component.html',
|
templateUrl: './view-carnet.component.html',
|
||||||
styleUrl: './view-carnet.component.scss'
|
styleUrl: './view-carnet.component.scss'
|
||||||
})
|
})
|
||||||
@ -25,12 +26,20 @@ export class ViewCarnetComponent {
|
|||||||
headerid: number = 0;
|
headerid: number = 0;
|
||||||
applicationName: string = '';
|
applicationName: string = '';
|
||||||
userPreferences: UserPreferences;
|
userPreferences: UserPreferences;
|
||||||
|
applicationType: 'new' | 'edit' | 'additional' | 'duplicate' | 'extend' | null = 'edit';
|
||||||
|
|
||||||
private route = inject(ActivatedRoute);
|
private route = inject(ActivatedRoute);
|
||||||
|
|
||||||
constructor(userPrefenceService: UserPreferencesService) {
|
constructor(userPrefenceService: UserPreferencesService) {
|
||||||
this.userPreferences = userPrefenceService.getPreferences();
|
this.userPreferences = userPrefenceService.getPreferences();
|
||||||
|
|
||||||
|
// set application type based on the route url
|
||||||
|
const urlSegments = this.route.snapshot.url.map(segment => segment.path);
|
||||||
|
|
||||||
|
if (urlSegments.includes('view-extend-carnet')) {
|
||||||
|
this.applicationType = 'extend';
|
||||||
|
}
|
||||||
|
|
||||||
afterNextRender(() => {
|
afterNextRender(() => {
|
||||||
// Open all panels
|
// Open all panels
|
||||||
this.accordion().openAll();
|
this.accordion().openAll();
|
||||||
|
|||||||
15
src/app/core/models/carnet/replacement.ts
Normal file
15
src/app/core/models/carnet/replacement.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
export interface Replacement {
|
||||||
|
replacementCarnetNumber: string;
|
||||||
|
issueDate: Date;
|
||||||
|
preparerName: string;
|
||||||
|
address1: string;
|
||||||
|
address2?: string | null;
|
||||||
|
city: string;
|
||||||
|
state: string;
|
||||||
|
country: string;
|
||||||
|
zip: string;
|
||||||
|
portName: string;
|
||||||
|
portCountry: string;
|
||||||
|
reasonForExtension: string;
|
||||||
|
duration: number;
|
||||||
|
}
|
||||||
5
src/app/core/models/extension-reason.ts
Normal file
5
src/app/core/models/extension-reason.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export interface ExtensionReason {
|
||||||
|
name: string;
|
||||||
|
id: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
59
src/app/core/services/carnet/replacement.service.ts
Normal file
59
src/app/core/services/carnet/replacement.service.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { inject, Injectable } from '@angular/core';
|
||||||
|
import { environment } from '../../../../environments/environment';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { map, Observable } from 'rxjs';
|
||||||
|
import { UserService } from '../common/user.service';
|
||||||
|
import { Replacement } from '../../models/carnet/replacement';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class ReplacementService {
|
||||||
|
private apiUrl = environment.apiUrl;
|
||||||
|
private apiDb = environment.apiDb;
|
||||||
|
|
||||||
|
private http = inject(HttpClient);
|
||||||
|
private userService = inject(UserService);
|
||||||
|
|
||||||
|
getExtensionData(headerid: number): Replacement | any {
|
||||||
|
return this.http.get<any[]>(`${this.apiUrl}/${this.apiDb}/GetExtendedSection/${this.userService.getUserSpid()}/${headerid}`).pipe(
|
||||||
|
map(response => this.mapToExtensionData(response)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private mapToExtensionData(extensionDataCollection: any): Replacement {
|
||||||
|
let extensionData = extensionDataCollection?.[0];
|
||||||
|
|
||||||
|
let replacementData: Replacement = {
|
||||||
|
replacementCarnetNumber: extensionData.REPLACECARNETNO,
|
||||||
|
issueDate: extensionData.ISSUEDATE,
|
||||||
|
preparerName: extensionData.PREPARERNAME,
|
||||||
|
address1: extensionData.ADDRESS1,
|
||||||
|
address2: extensionData.ADDRESS2,
|
||||||
|
city: extensionData.CITY,
|
||||||
|
state: extensionData.STATE,
|
||||||
|
country: extensionData.COUNTRY,
|
||||||
|
zip: extensionData.ZIP,
|
||||||
|
portName: extensionData.GOODSPORT,
|
||||||
|
portCountry: extensionData.GOODSCOUNTRY,
|
||||||
|
reasonForExtension: extensionData.REASONCODE,
|
||||||
|
duration: extensionData.EXTENSIONPERIOD,
|
||||||
|
};
|
||||||
|
|
||||||
|
return replacementData;
|
||||||
|
}
|
||||||
|
|
||||||
|
saveExtensionDetails(headerid: number, extensionData: Replacement): Observable<any> {
|
||||||
|
|
||||||
|
const extensionDetails = {
|
||||||
|
P_HEADERID: headerid,
|
||||||
|
P_GOODSPORT: extensionData.portName,
|
||||||
|
P_GOODSCOUNTRY: extensionData.portCountry,
|
||||||
|
P_REASONCODE: extensionData.reasonForExtension,
|
||||||
|
P_EXTENSIONPERIOD: extensionData.duration,
|
||||||
|
P_USERID: this.userService.getUser(),
|
||||||
|
P_SPID: this.userService.getUserSpid()
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.http.patch(`${this.apiUrl}/${this.apiDb}/SaveExtensionApplication`, extensionDetails);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -17,6 +17,7 @@ import { DeliveryMethod } from '../../models/delivery-method';
|
|||||||
import { PaymentType } from '../../models/payment-type';
|
import { PaymentType } from '../../models/payment-type';
|
||||||
import { FormOfSecurity } from '../../models/formofsecurity';
|
import { FormOfSecurity } from '../../models/formofsecurity';
|
||||||
import { UserService } from './user.service';
|
import { UserService } from './user.service';
|
||||||
|
import { ExtensionReason } from '../../models/extension-reason';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -205,6 +206,18 @@ export class CommonService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getExtensionReasons(): Observable<ExtensionReason[]> {
|
||||||
|
return this.http.get<any[]>(`${this.apiUrl}/${this.apiDb}/GetParamValues?P_PARAMTYPE=008&P_SPID=${this.userService.getUserSpid()}`).pipe(
|
||||||
|
map((response) =>
|
||||||
|
response.map((item) => ({
|
||||||
|
name: item.PARAMDESC,
|
||||||
|
id: item.PARAMID,
|
||||||
|
value: item.PARAMVALUE,
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
formatUSDate(datetime: Date): string {
|
formatUSDate(datetime: Date): string {
|
||||||
const date = new Date(datetime);
|
const date = new Date(datetime);
|
||||||
const month = String(date.getUTCMonth() + 1).padStart(2, '0');
|
const month = String(date.getUTCMonth() + 1).padStart(2, '0');
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
import { Component, Inject } from '@angular/core';
|
import { Component, inject, Inject } from '@angular/core';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { ReactiveFormsModule } from '@angular/forms';
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
import { AngularMaterialModule } from '../shared/module/angular-material.module';
|
import { AngularMaterialModule } from '../shared/module/angular-material.module';
|
||||||
|
import { UserService } from '../core/services/common/user.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-bor-dialog',
|
selector: 'app-bor-dialog',
|
||||||
@ -28,12 +29,14 @@ export class BorDialogComponent {
|
|||||||
title: string = '';
|
title: string = '';
|
||||||
spName: string = '';
|
spName: string = '';
|
||||||
|
|
||||||
|
private userService = inject(UserService);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public dialogRef: MatDialogRef<BorDialogComponent>,
|
public dialogRef: MatDialogRef<BorDialogComponent>,
|
||||||
@Inject(MAT_DIALOG_DATA) public data: { carnetAction: string, spName: string },
|
@Inject(MAT_DIALOG_DATA) public data: { carnetAction: string },
|
||||||
) {
|
) {
|
||||||
this.carnetAction = data.carnetAction;
|
this.carnetAction = data.carnetAction;
|
||||||
this.spName = data.spName;
|
this.spName = this.userService.getUserDetails()?.userDetails?.serviceProviderName || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
|||||||
@ -174,7 +174,7 @@ export class HomeComponent {
|
|||||||
this.carnetService.additionalSets(carnetNumber).subscribe({
|
this.carnetService.additionalSets(carnetNumber).subscribe({
|
||||||
next: (data) => {
|
next: (data) => {
|
||||||
if (data && data.BORFLAG === 'Y') {
|
if (data && data.BORFLAG === 'Y') {
|
||||||
this.showBorDialog('additional', data.HEADERID, data.SPNAME, data.APPLICATIONNAME);
|
this.showBorDialog('additional', data.HEADERID, data.APPLICATIONNAME);
|
||||||
} else if (data) {
|
} else if (data) {
|
||||||
this.navigateTo(['additional-sets', data.HEADERID], {
|
this.navigateTo(['additional-sets', data.HEADERID], {
|
||||||
queryParams: { applicationname: data.APPLICATIONNAME }
|
queryParams: { applicationname: data.APPLICATIONNAME }
|
||||||
@ -204,7 +204,7 @@ export class HomeComponent {
|
|||||||
this.carnetService.duplicateCarnet(carnetNumber).subscribe({
|
this.carnetService.duplicateCarnet(carnetNumber).subscribe({
|
||||||
next: (data) => {
|
next: (data) => {
|
||||||
if (data && data.BORFLAG === 'Y') {
|
if (data && data.BORFLAG === 'Y') {
|
||||||
this.showBorDialog('duplicate', data.HEADERID, data.SPNAME, data.APPLICATIONNAME);
|
this.showBorDialog('duplicate', data.HEADERID, data.APPLICATIONNAME);
|
||||||
} else if (data) {
|
} else if (data) {
|
||||||
this.navigateTo(['duplicate-carnet', data.HEADERID], {
|
this.navigateTo(['duplicate-carnet', data.HEADERID], {
|
||||||
queryParams: { applicationname: data.APPLICATIONNAME }
|
queryParams: { applicationname: data.APPLICATIONNAME }
|
||||||
@ -234,7 +234,7 @@ export class HomeComponent {
|
|||||||
this.carnetService.extendCarnet(carnetNumber).subscribe({
|
this.carnetService.extendCarnet(carnetNumber).subscribe({
|
||||||
next: (data) => {
|
next: (data) => {
|
||||||
if (data && data.BORFLAG === 'Y') {
|
if (data && data.BORFLAG === 'Y') {
|
||||||
this.showBorDialog('extend', data.HEADERID, data.SPNAME, data.APPLICATIONNAME);
|
this.showBorDialog('extend', data.HEADERID, data.APPLICATIONNAME);
|
||||||
} else if (data) {
|
} else if (data) {
|
||||||
this.navigateTo(['extend-carnet', data.HEADERID], {
|
this.navigateTo(['extend-carnet', data.HEADERID], {
|
||||||
queryParams: { applicationname: data.APPLICATIONNAME }
|
queryParams: { applicationname: data.APPLICATIONNAME }
|
||||||
@ -252,7 +252,7 @@ export class HomeComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
showBorDialog(carnetAction: string, headerid: number, spName: string, applicationName: string): void {
|
showBorDialog(carnetAction: string, headerid: number, applicationName: string): void {
|
||||||
const dialogRef = this.dialog.open(BorDialogComponent, {
|
const dialogRef = this.dialog.open(BorDialogComponent, {
|
||||||
width: '500px',
|
width: '500px',
|
||||||
data: { carnetAction: carnetAction },
|
data: { carnetAction: carnetAction },
|
||||||
@ -309,7 +309,13 @@ export class HomeComponent {
|
|||||||
|
|
||||||
viewCarnet(item: any): void {
|
viewCarnet(item: any): void {
|
||||||
if (item && item.headerid) {
|
if (item && item.headerid) {
|
||||||
this.navigateTo(['view-carnet', item.headerid], {
|
let route: string = 'view-carnet';
|
||||||
|
|
||||||
|
if (item.orderType === 'REPLACE') {
|
||||||
|
route = 'view-extend-carnet';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.navigateTo([route, item.headerid], {
|
||||||
queryParams: { applicationname: item.applicationName }
|
queryParams: { applicationname: item.applicationName }
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user