diff --git a/src/app/app.routes.server.ts b/src/app/app.routes.server.ts
index 7785d80..7e81881 100644
--- a/src/app/app.routes.server.ts
+++ b/src/app/app.routes.server.ts
@@ -8,6 +8,7 @@ export const serverRoutes: ServerRoute[] = [
{ path: ':appId/add-carnet', renderMode: RenderMode.Client },
{ path: ':appId/edit-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/extend-carnet/:headerid', renderMode: RenderMode.Client },
{ path: ':appId/additional-sets/:headerid', renderMode: RenderMode.Client },
diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts
index be3b4a0..dc46514 100644
--- a/src/app/app.routes.ts
+++ b/src/app/app.routes.ts
@@ -18,6 +18,7 @@ export const routes: Routes = [
{ 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: '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: '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) },
diff --git a/src/app/carnet/additional-set/additional-set.component.html b/src/app/carnet/additional-set/additional-set.component.html
deleted file mode 100644
index be5a34f..0000000
--- a/src/app/carnet/additional-set/additional-set.component.html
+++ /dev/null
@@ -1 +0,0 @@
-
additional-set works!
diff --git a/src/app/carnet/additional-set/additional-set.component.scss b/src/app/carnet/additional-set/additional-set.component.scss
deleted file mode 100644
index e69de29..0000000
diff --git a/src/app/carnet/additional-set/additional-set.component.ts b/src/app/carnet/additional-set/additional-set.component.ts
deleted file mode 100644
index 833cf4f..0000000
--- a/src/app/carnet/additional-set/additional-set.component.ts
+++ /dev/null
@@ -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 {
-
-}
diff --git a/src/app/carnet/duplicate/duplicate.component.html b/src/app/carnet/duplicate/duplicate.component.html
deleted file mode 100644
index 5f32c08..0000000
--- a/src/app/carnet/duplicate/duplicate.component.html
+++ /dev/null
@@ -1 +0,0 @@
-duplicate works!
diff --git a/src/app/carnet/duplicate/duplicate.component.scss b/src/app/carnet/duplicate/duplicate.component.scss
deleted file mode 100644
index e69de29..0000000
diff --git a/src/app/carnet/duplicate/duplicate.component.ts b/src/app/carnet/duplicate/duplicate.component.ts
deleted file mode 100644
index be3b7bb..0000000
--- a/src/app/carnet/duplicate/duplicate.component.ts
+++ /dev/null
@@ -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 {
-
-}
diff --git a/src/app/carnet/edit/edit-carnet.component.html b/src/app/carnet/edit/edit-carnet.component.html
index 05f0aa1..978ca82 100644
--- a/src/app/carnet/edit/edit-carnet.component.html
+++ b/src/app/carnet/edit/edit-carnet.component.html
@@ -3,6 +3,15 @@
+
+
+ Extension Application
+
+
+
+
Application Name
diff --git a/src/app/carnet/edit/edit-carnet.component.ts b/src/app/carnet/edit/edit-carnet.component.ts
index 143072f..c25822a 100644
--- a/src/app/carnet/edit/edit-carnet.component.ts
+++ b/src/app/carnet/edit/edit-carnet.component.ts
@@ -11,11 +11,12 @@ import { ShippingComponent } from '../shipping/shipping.component';
import { TravelPlanComponent } from '../travel-plan/travel-plan.component';
import { UserPreferences } from '../../core/models/user-preference';
import { UserPreferencesService } from '../../core/services/user-preference.service';
+import { ReplacementSetComponent } from '../replacement-set/replacement-set.component';
@Component({
selector: 'app-edit-carnet',
imports: [AngularMaterialModule, CommonModule, ReactiveFormsModule,
- ApplicationComponent, HolderComponent, GoodsComponent, TravelPlanComponent, ShippingComponent],
+ ApplicationComponent, HolderComponent, GoodsComponent, TravelPlanComponent, ShippingComponent, ReplacementSetComponent],
templateUrl: './edit-carnet.component.html',
styleUrl: './edit-carnet.component.scss'
})
@@ -38,7 +39,8 @@ export class EditCarnetComponent {
holderSelection: false,
goodsSection: false,
travelPlan: false,
- shipping: false
+ shipping: false,
+ extension: false
};
private route = inject(ActivatedRoute);
@@ -70,6 +72,8 @@ export class EditCarnetComponent {
if (returnTo === 'holder') {
this.currentStep = 1;
+ } else if (returnTo === 'shipping' && this.applicationType === 'extend') {
+ this.currentStep = 5;
} else if (returnTo === 'shipping') {
this.currentStep = 4;
} else if (this.applicationType === 'additional') {
@@ -115,9 +119,15 @@ export class EditCarnetComponent {
this.shippingComponent?.refreshInsuranceData(data);
}
+ onExtensionApplicationSaved(completed: boolean): void {
+ this.stepsCompleted.extension = completed;
+ this.isAllSectionsCompleted();
+ }
+
isAllSectionsCompleted() {
this.allSectionsCompleted = this.stepsCompleted.applicationDetail
&& this.stepsCompleted.holderSelection && this.stepsCompleted.goodsSection
- && this.stepsCompleted.shipping && this.stepsCompleted.travelPlan;
+ && this.stepsCompleted.shipping && this.stepsCompleted.travelPlan
+ && (this.applicationType !== 'extend' || this.stepsCompleted.extension);
}
}
diff --git a/src/app/carnet/goods/goods.component.ts b/src/app/carnet/goods/goods.component.ts
index 9472d8d..084709a 100644
--- a/src/app/carnet/goods/goods.component.ts
+++ b/src/app/carnet/goods/goods.component.ts
@@ -33,8 +33,15 @@ export class GoodsComponent {
@Input() isEditMode: boolean = false;
@Output() completed = new EventEmitter();
- @ViewChild(MatPaginator) paginator!: MatPaginator;
- @ViewChild(MatSort) sort!: MatSort;
+ @ViewChild(MatPaginator, { static: false })
+ set paginator(value: MatPaginator) {
+ this.dataSource.paginator = value;
+ }
+
+ @ViewChild(MatSort, { static: false })
+ set sort(value: MatSort) {
+ this.dataSource.sort = value;
+ }
// Table configuration
displayedColumns: string[] = ['itemNumber', 'description', 'pieces', 'weight', 'unitOfMeasure', 'value', 'countryOfOrigin', 'actions'];
diff --git a/src/app/carnet/replacement-set/replacement-set.component.html b/src/app/carnet/replacement-set/replacement-set.component.html
index 6336602..88491b9 100644
--- a/src/app/carnet/replacement-set/replacement-set.component.html
+++ b/src/app/carnet/replacement-set/replacement-set.component.html
@@ -1 +1,98 @@
-replacement-set works!
+
+
+
+
+
+
+
+
+
General
+
+
+ {{ replacementDetails?.replacementCarnetNumber || 'N/A' }}
+
+
+
+
+ {{ replacementDetails?.issueDate | date:'mediumDate':'UTC' }}
+
+
+
+
+ {{ replacementDetails?.preparerName || 'N/A' }}
+
+
+
+
+ {{ getAddressLabel() || 'N/A' }}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/app/carnet/replacement-set/replacement-set.component.scss b/src/app/carnet/replacement-set/replacement-set.component.scss
index e69de29..3ad1d3f 100644
--- a/src/app/carnet/replacement-set/replacement-set.component.scss
+++ b/src/app/carnet/replacement-set/replacement-set.component.scss
@@ -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;
+ }
+}
\ No newline at end of file
diff --git a/src/app/carnet/replacement-set/replacement-set.component.ts b/src/app/carnet/replacement-set/replacement-set.component.ts
index f3fc06b..6472e05 100644
--- a/src/app/carnet/replacement-set/replacement-set.component.ts
+++ b/src/app/carnet/replacement-set/replacement-set.component.ts
@@ -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({
selector: 'app-replacement-set',
- imports: [],
+ imports: [AngularMaterialModule, CommonModule, ReactiveFormsModule],
templateUrl: './replacement-set.component.html',
styleUrl: './replacement-set.component.scss'
})
export class ReplacementSetComponent {
+ @Input() headerid: number = 0;
+ @Input() isViewMode = false;
+ @Input() isEditMode: boolean = false;
+ @Output() completed = new EventEmitter();
-}
+ extensionForm: FormGroup;
+ countries: Country[] = [];
+ extensionReasons: ExtensionReason[] = [];
+ replacementDetails: Replacement | undefined;
+
+ // UI state
+ isEditing = false;
+ isLoading = false;
+ changeInProgress = false;
+
+ private destroy$ = new Subject();
+
+ 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(', ') || '';
+ }
+}
\ No newline at end of file
diff --git a/src/app/carnet/shipping/shipping.component.ts b/src/app/carnet/shipping/shipping.component.ts
index f7015ff..c69bbdd 100644
--- a/src/app/carnet/shipping/shipping.component.ts
+++ b/src/app/carnet/shipping/shipping.component.ts
@@ -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.changeInProgress = false;
})).subscribe({
@@ -483,9 +488,13 @@ export class ShippingComponent {
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('needsLostDocProtection')?.disable();
+ }
+
+ if (this.applicationType === 'duplicate' || this.applicationType === 'additional') {
this.shippingForm.get('needsInsurance')?.disable();
}
}
diff --git a/src/app/carnet/view/view-carnet.component.html b/src/app/carnet/view/view-carnet.component.html
index 6b9b54e..4b44e5a 100644
--- a/src/app/carnet/view/view-carnet.component.html
+++ b/src/app/carnet/view/view-carnet.component.html
@@ -3,6 +3,15 @@