diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 3d3fa74..3b1b0e6 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -1,20 +1,19 @@ import { Routes } from '@angular/router'; import { AppIdGuard } from './guards/appid.guard'; import { AuthGuard } from './guards/auth.guard'; -import { HomeComponent } from './home/home.component'; -import { LoginComponent } from './login/login.component'; -import { NotFoundComponent } from './shared/components/not-found/not-found.component'; -import { UserSettingsComponent } from './user-settings/user-settings.component'; export const routes: Routes = [ - { path: 'login', component: LoginComponent }, - { path: '404', component: NotFoundComponent }, + { path: 'login', loadComponent: () => import('./login/login.component').then(m => m.LoginComponent) }, + { path: 'forgot-password', loadComponent: () => import('./forgot-password/forgot-password.component').then(m => m.ForgotPasswordComponent) }, + { path: 'register/:token', loadComponent: () => import('./register/register.component').then(m => m.RegisterComponent) }, + { path: '404', loadComponent: () => import('./shared/components/not-found/not-found.component').then(m => m.NotFoundComponent) }, { path: ':appId', children: [ - { path: 'home', component: HomeComponent }, - { path: 'usersettings', component: UserSettingsComponent }, + { path: 'home', loadComponent: () => import('./home/home.component').then(m => m.HomeComponent) }, + { path: 'usersettings', loadComponent: () => import('./user-settings/user-settings.component').then(m => m.UserSettingsComponent) }, { path: 'add-carnet', loadComponent: () => import('./carnet/add/add-carnet.component').then(m => m.AddCarnetComponent) }, + { path: 'edit-carnet/:headerid', loadComponent: () => import('./carnet/edit/edit-carnet.component').then(m => m.EditCarnetComponent) }, { 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 19d438c..4422733 100644 --- a/src/app/carnet/add/add-carnet.component.html +++ b/src/app/carnet/add/add-carnet.component.html @@ -1,64 +1,21 @@
- -
-

Add New Carnet

- -
-
- - Do you need a new carnet? - Yes - No - -
- -
- - Do you need additional sets? - Yes - No - -
- -
- - Did you lose your carnet? Are you looking for duplicates? - Yes - No - -
- -
- - Do you need to extend the carnet? - Yes - No - -
- -
- -
-
-
- - + - + Application Name - + - + Holder Selection - + @@ -66,44 +23,45 @@ Goods Section - + - Travel Plan - + + - + Insurance - + + Shipping - + + Delivery Method - + + Payment - + +
\ No newline at end of file diff --git a/src/app/carnet/add/add-carnet.component.scss b/src/app/carnet/add/add-carnet.component.scss index f238891..c7e2a38 100644 --- a/src/app/carnet/add/add-carnet.component.scss +++ b/src/app/carnet/add/add-carnet.component.scss @@ -1,51 +1,11 @@ -.client-carnet-container { - // padding: 24px; - // max-width: 1200px; - // margin: 0 auto; +// .client-carnet-container { +// // padding: 24px; +// // max-width: 1200px; +// // margin: 0 auto; - .questions-section { - // padding: 20px; - // border-radius: 8px; - // margin-bottom: 24px; - - // h2 { - // margin-bottom: 24px; - // color: #3f51b5; - // } - - .question-row { - margin-bottom: 1rem; - padding: 1rem; - background: #ffff; - border-radius: 4px; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - - mat-radio-group { - display: flex; - flex-direction: column; - gap: 8px; - - // mat-label { - // margin-bottom: 8px; - // font-weight: 500; - // } - - mat-radio-button { - margin-right: 1rem; - } - } - } - - .actions { - margin-top: 24px; - display: flex; - justify-content: flex-end; - } - } - - .actions { - margin-top: 24px; - display: flex; - justify-content: flex-start; - } -} \ No newline at end of file +// .actions { +// margin-top: 24px; +// display: flex; +// justify-content: flex-start; +// } +// } \ 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 f84efb3..671d65b 100644 --- a/src/app/carnet/add/add-carnet.component.ts +++ b/src/app/carnet/add/add-carnet.component.ts @@ -6,31 +6,26 @@ import { StepperSelectionEvent } from '@angular/cdk/stepper'; import { ApplicationComponent } from "../application/application.component"; import { HolderComponent } from '../holder/holder.component'; import { GoodsComponent } from '../goods/goods.component'; +import { TravelPlanComponent } from '../travel-plan/travel-plan.component'; +import { InsuranceComponent } from '../insurance/insurance.component'; +import { PaymentComponent } from '../payment/payment.component'; +import { ShippingComponent } from '../shipping/shipping.component'; +import { DeliveryComponent } from '../delivery/delivery.component'; @Component({ selector: 'app-add-carnet', imports: [AngularMaterialModule, CommonModule, ReactiveFormsModule, - ApplicationComponent, HolderComponent, GoodsComponent], + ApplicationComponent, HolderComponent, GoodsComponent, TravelPlanComponent, InsuranceComponent, + ShippingComponent, DeliveryComponent, PaymentComponent], templateUrl: './add-carnet.component.html', styleUrl: './add-carnet.component.scss' }) export class AddCarnetComponent { currentStep = 0; - questionsCompleted = false; isLinear = true; - applicationType: 'new' | 'additional' | 'duplicate' | 'extend' | null = null; + applicationType: 'new' | 'additional' | 'duplicate' | 'extend' | null = 'new'; isEditMode = false; - applicationid: number = 0; - - private fb = inject(FormBuilder); - - // Form group for initial questions - questionsForm: FormGroup = this.fb.group({ - newCarnet: [null, Validators.required], - additionalSets: [false], - duplicateCarnet: [false], - extendCarnet: [false] - }); + headerid: number = 0; // Track completion of each step stepsCompleted = { @@ -44,30 +39,12 @@ export class AddCarnetComponent { payment: false }; - onQuestionsSubmit(): void { - const answers = this.questionsForm.value; - - if (answers.newCarnet) { - this.applicationType = 'new'; - } else if (answers.additionalSets) { - this.applicationType = 'additional'; - } else if (answers.duplicateCarnet) { - this.applicationType = 'duplicate'; - } else if (answers.extendCarnet) { - this.applicationType = 'extend'; - } - - // Move to the first step after questions - this.questionsCompleted = true; - this.currentStep = 0; - } - onStepChange(event: StepperSelectionEvent): void { this.currentStep = event.selectedIndex; } - onApplicationDetailCreated(applicationid: number): void { - this.applicationid = applicationid; + onApplicationDetailCreated(headerid: number): void { + this.headerid = this.headerid; this.stepsCompleted.applicationDetail = true; this.isLinear = false; // Disable linear mode after application detail is created } diff --git a/src/app/carnet/application/application.component.ts b/src/app/carnet/application/application.component.ts index 5507eb0..e098ff8 100644 --- a/src/app/carnet/application/application.component.ts +++ b/src/app/carnet/application/application.component.ts @@ -7,6 +7,7 @@ import { ApplicationDetail } from '../../core/models/carnet/application-detail'; import { Subject } from 'rxjs'; import { AngularMaterialModule } from '../../shared/module/angular-material.module'; import { CommonModule } from '@angular/common'; +import { ActivatedRoute } from '@angular/router'; @Component({ selector: 'app-application', @@ -16,9 +17,10 @@ import { CommonModule } from '@angular/common'; }) export class ApplicationComponent { @Input() isEditMode = false; - @Input() applicationid: number = 0; + @Input() headerid: number = 0; + @Input() applicationName: string = ''; - @Output() applicationIdCreated = new EventEmitter(); + @Output() headerIdCreated = new EventEmitter(); applicationDetailsForm: FormGroup; isLoading = false; @@ -30,31 +32,33 @@ export class ApplicationComponent { private applicationDetailService = inject(ApplicationDetailService); private notificationService = inject(NotificationService); private errorHandler = inject(ApiErrorHandlerService); + private route = inject(ActivatedRoute); constructor() { this.applicationDetailsForm = this.createForm(); } ngOnInit(): void { - // this.spidCreated.emit(this.spid?.toString()); + this.applicationName = this.route.snapshot.queryParamMap.get('applicationName') || ''; // Patch edit form data - if (this.applicationid > 0) { - this.isLoading = true; - this.applicationDetailService.getApplicationDetailsById(this.applicationid).subscribe({ - next: (applicationDetail: ApplicationDetail) => { - if (applicationDetail?.applicationId > 0) { - this.patchFormData(applicationDetail); - } - this.isLoading = false; - }, - error: (error: any) => { - let errorMessage = this.errorHandler.handleApiError(error, 'Failed to load application details'); - this.notificationService.showError(errorMessage); - this.isLoading = false; - console.error('Error loading application details:', error); - } - }); + if (this.applicationName) { + this.patchFormData(); + + // this.applicationDetailService.getApplicationDetailsById(this.headerid).subscribe({ + // next: (applicationDetail: ApplicationDetail) => { + // if (applicationDetail?.applicationId > 0) { + // this.patchFormData(applicationDetail); + // } + // this.isLoading = false; + // }, + // error: (error: any) => { + // let errorMessage = this.errorHandler.handleApiError(error, 'Failed to load application details'); + // this.notificationService.showError(errorMessage); + // this.isLoading = false; + // console.error('Error loading application details:', error); + // } + // }); } } @@ -69,9 +73,9 @@ export class ApplicationComponent { }); } - patchFormData(data: ApplicationDetail): void { + patchFormData(): void { this.applicationDetailsForm.patchValue({ - name: data.name, + name: this.applicationName, }); if (this.isEditMode) { @@ -93,12 +97,12 @@ export class ApplicationComponent { const applicationDetailData: ApplicationDetail = this.applicationDetailsForm.value; - if (!this.isEditMode && this.applicationid == 0) { + if (!this.isEditMode && this.headerid == 0) { this.isLoading = true; this.applicationDetailService.createApplicationDetails(applicationDetailData).subscribe({ next: (applicationData: any) => { this.notificationService.showSuccess(`Application details added successfully`); - this.applicationIdCreated.emit(applicationData.P_HEADERID); + this.headerIdCreated.emit(applicationData.P_HEADERID); this.applicationDetailsForm.get('name')?.disable(); this.disableSaveButton = true; this.isLoading = false; diff --git a/src/app/carnet/delivery/delivery.component.html b/src/app/carnet/delivery/delivery.component.html index 829c8f8..e69de29 100644 --- a/src/app/carnet/delivery/delivery.component.html +++ b/src/app/carnet/delivery/delivery.component.html @@ -1 +0,0 @@ -

delivery works!

diff --git a/src/app/carnet/delivery/delivery.component.ts b/src/app/carnet/delivery/delivery.component.ts index 81169ad..8ccfec4 100644 --- a/src/app/carnet/delivery/delivery.component.ts +++ b/src/app/carnet/delivery/delivery.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; @Component({ selector: 'app-delivery', @@ -7,5 +7,7 @@ import { Component } from '@angular/core'; styleUrl: './delivery.component.scss' }) export class DeliveryComponent { + @Input() headerid: number = 0; + @Output() completed = new EventEmitter(); } diff --git a/src/app/carnet/edit/edit-carnet.component.html b/src/app/carnet/edit/edit-carnet.component.html index f889f50..6615637 100644 --- a/src/app/carnet/edit/edit-carnet.component.html +++ b/src/app/carnet/edit/edit-carnet.component.html @@ -1 +1,67 @@ -

edit-carnet works!

+
+ + + + + + Application Name + + + + + + + Holder Selection + + + + + + + Goods Section + + + + + + + Travel Plan + + + + + + + Insurance + + + + + + + Shipping + + + + + + + Delivery Method + + + + + + + Payment + + + + +
\ 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 b2a84e4..fa3f043 100644 --- a/src/app/carnet/edit/edit-carnet.component.ts +++ b/src/app/carnet/edit/edit-carnet.component.ts @@ -1,11 +1,81 @@ -import { Component } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Component, inject } from '@angular/core'; +import { AngularMaterialModule } from '../../shared/module/angular-material.module'; +import { ReactiveFormsModule } from '@angular/forms'; +import { ApplicationComponent } from '../application/application.component'; +import { StepperSelectionEvent } from '@angular/cdk/stepper'; +import { GoodsComponent } from '../goods/goods.component'; +import { HolderComponent } from '../holder/holder.component'; +import { ActivatedRoute } from '@angular/router'; +import { DeliveryComponent } from '../delivery/delivery.component'; +import { InsuranceComponent } from '../insurance/insurance.component'; +import { PaymentComponent } from '../payment/payment.component'; +import { ShippingComponent } from '../shipping/shipping.component'; +import { TravelPlanComponent } from '../travel-plan/travel-plan.component'; @Component({ selector: 'app-edit-carnet', - imports: [], + imports: [AngularMaterialModule, CommonModule, ReactiveFormsModule, + ApplicationComponent, HolderComponent, GoodsComponent, TravelPlanComponent, InsuranceComponent, + ShippingComponent, DeliveryComponent, PaymentComponent], templateUrl: './edit-carnet.component.html', styleUrl: './edit-carnet.component.scss' }) export class EditCarnetComponent { + currentStep = 0; + isLinear = false; + applicationType: 'new' | 'additional' | 'duplicate' | 'extend' | null = 'new'; + isEditMode = true; // Set to true for edit mode + headerid: number = 0; + // Track completion of each step + stepsCompleted = { + applicationDetail: true, + holderSelection: false, + goodsSection: false, + travelPlan: false, + insurance: false, + shipping: false, + deliveryMethod: false, + payment: false + }; + + private route = inject(ActivatedRoute); + + ngOnInit(): void { + const idParam = this.route.snapshot.paramMap.get('headerid'); + this.headerid = idParam ? parseInt(idParam, 10) : 0; + } + + onStepChange(event: StepperSelectionEvent): void { + this.currentStep = event.selectedIndex; + } + + onHolderSelectionSaved(completed: boolean): void { + this.stepsCompleted.holderSelection = completed; + } + + onGoodsSectionSaved(completed: boolean): void { + this.stepsCompleted.goodsSection = completed; + } + + onTravelPlanSaved(completed: boolean): void { + this.stepsCompleted.travelPlan = completed; + } + + onInsuranceSaved(completed: boolean): void { + this.stepsCompleted.insurance = completed; + } + + onShippingSaved(completed: boolean): void { + this.stepsCompleted.shipping = completed; + } + + onDeliveryMethodSaved(completed: boolean): void { + this.stepsCompleted.deliveryMethod = completed; + } + + onPaymentSaved(completed: boolean): void { + this.stepsCompleted.payment = completed; + } } diff --git a/src/app/carnet/goods/goods.component.ts b/src/app/carnet/goods/goods.component.ts index 9017e4b..3c870cc 100644 --- a/src/app/carnet/goods/goods.component.ts +++ b/src/app/carnet/goods/goods.component.ts @@ -9,6 +9,6 @@ import { CommonModule } from '@angular/common'; styleUrl: './goods.component.scss' }) export class GoodsComponent { - @Input() applicationid: number = 0; + @Input() headerid: number = 0; @Output() completed = new EventEmitter(); } diff --git a/src/app/carnet/holder/holder.component.ts b/src/app/carnet/holder/holder.component.ts index 4de42e6..2542504 100644 --- a/src/app/carnet/holder/holder.component.ts +++ b/src/app/carnet/holder/holder.component.ts @@ -7,6 +7,6 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; styleUrl: './holder.component.scss' }) export class HolderComponent { - @Input() applicationid: number = 0; + @Input() headerid: number = 0; @Output() completed = new EventEmitter(); } diff --git a/src/app/carnet/insurance/insurance.component.ts b/src/app/carnet/insurance/insurance.component.ts index 15fc95e..353e5cf 100644 --- a/src/app/carnet/insurance/insurance.component.ts +++ b/src/app/carnet/insurance/insurance.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; @Component({ selector: 'app-insurance', @@ -7,5 +7,6 @@ import { Component } from '@angular/core'; styleUrl: './insurance.component.scss' }) export class InsuranceComponent { - + @Input() headerid: number = 0; + @Output() completed = new EventEmitter(); } diff --git a/src/app/carnet/payment/payment.component.ts b/src/app/carnet/payment/payment.component.ts index 34cded1..2637d52 100644 --- a/src/app/carnet/payment/payment.component.ts +++ b/src/app/carnet/payment/payment.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; @Component({ selector: 'app-payment', @@ -7,5 +7,6 @@ import { Component } from '@angular/core'; styleUrl: './payment.component.scss' }) export class PaymentComponent { - + @Input() headerid: number = 0; + @Output() completed = new EventEmitter(); } diff --git a/src/app/carnet/shipping/shipping.component.ts b/src/app/carnet/shipping/shipping.component.ts index b7e504b..a972ee4 100644 --- a/src/app/carnet/shipping/shipping.component.ts +++ b/src/app/carnet/shipping/shipping.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; @Component({ selector: 'app-shipping', @@ -7,5 +7,6 @@ import { Component } from '@angular/core'; styleUrl: './shipping.component.scss' }) export class ShippingComponent { - + @Input() headerid: number = 0; + @Output() completed = new EventEmitter(); } diff --git a/src/app/carnet/travel-plan/travel-plan.component.ts b/src/app/carnet/travel-plan/travel-plan.component.ts index 686bbfc..6eeda3a 100644 --- a/src/app/carnet/travel-plan/travel-plan.component.ts +++ b/src/app/carnet/travel-plan/travel-plan.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; @Component({ selector: 'app-travel-plan', @@ -7,5 +7,6 @@ import { Component } from '@angular/core'; styleUrl: './travel-plan.component.scss' }) export class TravelPlanComponent { - + @Input() headerid: number = 0; + @Output() completed = new EventEmitter(); } diff --git a/src/app/core/interceptors/auth.interceptor.ts b/src/app/core/interceptors/auth.interceptor.ts index 575b438..cd163ca 100644 --- a/src/app/core/interceptors/auth.interceptor.ts +++ b/src/app/core/interceptors/auth.interceptor.ts @@ -7,6 +7,7 @@ import { AuthService } from '../services/common/auth.service'; @Injectable() export class AuthInterceptor implements HttpInterceptor { + private excludedUrls = ['/register', '/forgot-password']; private isRefreshing = false; private refreshTokenSubject: BehaviorSubject = new BehaviorSubject(null); @@ -17,7 +18,11 @@ export class AuthInterceptor implements HttpInterceptor { intercept(request: HttpRequest, next: HttpHandler): Observable> { - // Add withCredentials to all requests + // Add withCredentials to all requests except excluded URLs + if (this.excludedUrls.some(url => request.url.includes(url))) { + return next.handle(request); + } + request = request.clone({ withCredentials: true }); diff --git a/src/app/core/models/user.ts b/src/app/core/models/user.ts index 837eb73..fb70884 100644 --- a/src/app/core/models/user.ts +++ b/src/app/core/models/user.ts @@ -13,6 +13,7 @@ export interface Menu { export interface UserDetail { spid: number; clientid: number; + locationid: number; urlKey: string; logoName: string; themeName: string; diff --git a/src/app/core/services/carnet/application-detail.service.ts b/src/app/core/services/carnet/application-detail.service.ts index 5b32502..a4fd77b 100644 --- a/src/app/core/services/carnet/application-detail.service.ts +++ b/src/app/core/services/carnet/application-detail.service.ts @@ -15,29 +15,37 @@ 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]))); - } + // 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 - }; - } + // 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(); + + if (!userDetails || !userDetails.userDetails) { + throw new Error('User details are not available'); + } + const applicationDetails = { - P_SPID: this.userService.getUserSpid(), - P_CLIENTID: this.userService.getUserClientid(), + P_SPID: userDetails.userDetails.spid, + P_CLIENTID: userDetails.userDetails.clientid, + P_LOCATIONID: userDetails.userDetails.locationid, P_APPLICATIONNAME: data.name, + P_ORDERTYPE: 'ORIGINAL', P_USERID: this.userService.getUser(), } - return this.http.post(`${this.apiUrl}/${this.apiDb}/SaveCarnetApplication`, applicationDetails); + return this.http.post(`${this.apiUrl}/${this.apiDb}/CreateApplication`, applicationDetails); } } diff --git a/src/app/core/services/common/auth.service.ts b/src/app/core/services/common/auth.service.ts index 4128df0..7fbc112 100644 --- a/src/app/core/services/common/auth.service.ts +++ b/src/app/core/services/common/auth.service.ts @@ -1,4 +1,4 @@ -import { HttpClient } from '@angular/common/http'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { environment } from '../../../../environments/environment'; @@ -48,4 +48,25 @@ export class AuthService { }); } } + + register(email: string, password: string, token: string | null): Observable { + let headers = new HttpHeaders(); + + if (token) { + headers = headers.set('Authorization', `Bearer ${token}`); + } + + return this.http.post( + `${this.apiUrl}/register`, + { P_EMAILADDR: email, P_PASSWORD: password }, + { headers } + ); + } + + forgotPassword(email: string, password: string): Observable { + return this.http.post( + `${this.apiUrl}/forgot-password`, + { P_EMAILADDR: email, P_PASSWORD: password } + ); + } } diff --git a/src/app/core/services/common/user.service.ts b/src/app/core/services/common/user.service.ts index 759f18f..89972d9 100644 --- a/src/app/core/services/common/user.service.ts +++ b/src/app/core/services/common/user.service.ts @@ -12,9 +12,6 @@ export class UserService { private apiUrl = environment.apiUrl; private apiDb = environment.apiDb; - private spid: number = 0; - private clientid: number = 0; - userDetailsSignal = signal({}); private readonly USER_EMAIL_KEY = 'CurrentUserEmail'; @@ -98,23 +95,18 @@ export class UserService { } getUserSpid(): number { - if (this.spid === 0) { - const userDetails = this.getUserDetails(); - if (userDetails && userDetails.userDetails && userDetails.userDetails.spid) { - this.spid = userDetails.userDetails.spid; - } - } - return this.spid; + const userDetails = this.getUserDetails(); + return userDetails?.userDetails?.spid ?? 0; } getUserClientid(): number { - if (this.clientid === 0) { - const userDetails = this.getUserDetails(); - if (userDetails && userDetails.userDetails && userDetails.userDetails.clientid) { - this.clientid = userDetails.userDetails.clientid; - } - } - return this.clientid; + const userDetails = this.getUserDetails(); + return userDetails?.userDetails?.clientid ?? 0; + } + + getUserLocationid(): number { + const userDetails = this.getUserDetails(); + return userDetails?.userDetails?.locationid ?? 0; } private mapToUser(data: any): User { @@ -141,6 +133,7 @@ export class UserService { return { spid: userDetails.SPID, clientid: userDetails.CLIENTID, + locationid: userDetails.LOCATIONID, urlKey: userDetails.ENCURLKEY, logoName: userDetails.LOGONAME, themeName: userDetails.THEMENAME diff --git a/src/app/core/services/home.service.ts b/src/app/core/services/home.service.ts index 8ecee78..9c71dad 100644 --- a/src/app/core/services/home.service.ts +++ b/src/app/core/services/home.service.ts @@ -37,7 +37,8 @@ export class HomeService { issueDate: item.ISSUEDATE || null, expiryDate: item.EXPDATE || null, orderType: item.ORDERTYPE, - carnetStatus: item.CARNETSTATUS + carnetStatus: item.CARNETSTATUS, + headerId: item.HEADERID, })); } } diff --git a/src/app/forgot-password/forgot-password.component.html b/src/app/forgot-password/forgot-password.component.html new file mode 100644 index 0000000..2968847 --- /dev/null +++ b/src/app/forgot-password/forgot-password.component.html @@ -0,0 +1,56 @@ +
+
+

Forgot Password

+ +
+ + Email + + email + + Email is required + + + Please enter a valid email + + + + + Password + + + + Password is required + + + + + + + +
+
+ +
+

ATA Carnet

+

+ Also known as the "Merchandise Passport," is an international customs document that simplifies temporary + exports to over 79 countries and territories. + It allows businesses to explore new markets, showcase products at trade shows, and attend global conferences + without paying duties or taxes. +

+

+ It simplifies customs procedures for the temporary movement of goods and allows goods to + enter Customs territories of the ATA Carnet system free of customs duties and taxes for up to + one year. +

+
+
\ No newline at end of file diff --git a/src/app/forgot-password/forgot-password.component.scss b/src/app/forgot-password/forgot-password.component.scss new file mode 100644 index 0000000..4415baa --- /dev/null +++ b/src/app/forgot-password/forgot-password.component.scss @@ -0,0 +1,95 @@ +/* forgot-password.component.scss */ +.forgot-password-container { + display: flex; + min-height: 85vh; + background-color: #f5f5f5; + + .forgot-password-card { + flex: 1; + max-width: 500px; + padding: 2rem; + display: flex; + flex-direction: column; + justify-content: center; + background-color: white; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + + .subtitle { + text-align: center; + color: #666; + font-size: 1.5rem; + margin-bottom: 2rem; + } + + .forgot-password-form { + display: flex; + flex-direction: column; + gap: 1.5rem; + + mat-form-field { + width: 100%; + } + + .login { + text-align: right; + margin-top: -1rem; + + .login-link { + color: #666; + text-decoration: none; + font-size: 0.875rem; + + &:hover { + text-decoration: underline; + } + } + } + + .mdc-icon-button { + margin-right: 8px; + } + + // button { + // margin-top: 1rem; + // padding: 0.5rem; + // font-size: 1rem; + // } + } + } + + .info-section { + flex: 1; + padding: 4rem; + background-color: #597b7c; + color: white; + display: flex; + flex-direction: column; + justify-content: center; + + h3 { + font-size: 2rem; + margin-bottom: 1.5rem; + } + + p { + line-height: 1.6; + font-size: 1rem; + } + } +} + +@media (max-width: 768px) { + .forgot-password-container { + flex-direction: column; + + .forgot-password-card { + max-width: 100%; + padding: 1.5rem; + } + + .info-section { + padding: 2rem; + display: none; + } + } +} \ No newline at end of file diff --git a/src/app/forgot-password/forgot-password.component.ts b/src/app/forgot-password/forgot-password.component.ts new file mode 100644 index 0000000..e63d864 --- /dev/null +++ b/src/app/forgot-password/forgot-password.component.ts @@ -0,0 +1,71 @@ +import { Component, inject } from '@angular/core'; +import { FormGroup, FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms'; +import { Router } from '@angular/router'; +import { ApiErrorHandlerService } from '../core/services/common/api-error-handler.service'; +import { AuthService } from '../core/services/common/auth.service'; +import { NotificationService } from '../core/services/common/notification.service'; +import { ThemeService } from '../core/services/theme.service'; +import { AngularMaterialModule } from '../shared/module/angular-material.module'; +import { CommonModule } from '@angular/common'; + +@Component({ + selector: 'app-forgot-password', + imports: [AngularMaterialModule, ReactiveFormsModule, CommonModule], + templateUrl: './forgot-password.component.html', + styleUrl: './forgot-password.component.scss' +}) +export class ForgotPasswordComponent { + + forgotPasswordForm: FormGroup; + isLoading = false; + hidePassword = true; + + private fb = inject(FormBuilder); + private authService = inject(AuthService); + private router = inject(Router); + private notificationService = inject(NotificationService); + private errorHandler = inject(ApiErrorHandlerService); + private themeService = inject(ThemeService); + + constructor() { + this.forgotPasswordForm = this.fb.group({ + email: ['', [Validators.required, Validators.email]], + password: ['', [Validators.required]] + }); + } + + ngOnInit(): void { + this.themeService.setTheme('default'); + } + + onSubmit(): void { + if (this.forgotPasswordForm.invalid) { + this.forgotPasswordForm.markAllAsTouched(); + return; + } + + this.isLoading = true; + const { email, password } = this.forgotPasswordForm.value; + + this.authService.forgotPassword(email, password).subscribe({ + next: () => { + this.notificationService.showSuccess('Request successful'); + this.router.navigate(['/login']); + }, + error: (error) => { + this.isLoading = false; + let errorMessage = this.errorHandler.handleApiError(error, `Request failed. Please try again.`); + this.notificationService.showError(errorMessage); + console.error('Forgot password request failed:', error); + } + }); + } + + get email() { + return this.forgotPasswordForm.get('email'); + } + + get password() { + return this.forgotPasswordForm.get('password'); + } +} diff --git a/src/app/home/chart/chart.component.ts b/src/app/home/chart/chart.component.ts index 23fd52b..c73b651 100644 --- a/src/app/home/chart/chart.component.ts +++ b/src/app/home/chart/chart.component.ts @@ -5,6 +5,7 @@ import { BaseChartDirective } from 'ng2-charts'; import { AngularMaterialModule } from '../../shared/module/angular-material.module'; import { CarnetStatus } from '../../core/models/carnet-status'; import { NavigationService } from '../../core/services/common/navigation.service'; +import { UserService } from '../../core/services/common/user.service'; @Component({ selector: 'app-chart', @@ -18,6 +19,7 @@ export class ChartComponent { @Output() carnetStatusData = new EventEmitter(); private navigationService = inject(NavigationService); + private userService = inject(UserService); navigateToManageProvider(spid: number): void { this.navigationService.navigate(['service-provider', spid]); @@ -98,8 +100,8 @@ export class ChartComponent { }); return { - title: provider.Service_Provider_Name, - spid: provider.SPID, + title: provider.Service_Provider_Name ?? provider.Preparer_Name, + spid: provider.SPID ?? this.userService.getUserSpid(), data: { labels: labels, datasets: [{ diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html index cd1eae5..2e194fe 100644 --- a/src/app/home/home.component.html +++ b/src/app/home/home.component.html @@ -1,15 +1,46 @@
-
- -
+
+ +
+ +
- -
- + +
+
+ + Do you need a new carnet? + Yes + No + +
+ +
+ + Do you need additional sets? + Yes + No + +
+ +
+ + Did you lose your carnet? Are you looking for duplicates? + Yes + No + +
+ +
+ + Do you need to extend the carnet? + Yes + No + +
+
@@ -79,7 +110,7 @@ Actions - + + Password is required + + + + + + + + +
+ +
+

ATA Carnet

+

+ Also known as the "Merchandise Passport," is an international customs document that simplifies temporary + exports to over 79 countries and territories. + It allows businesses to explore new markets, showcase products at trade shows, and attend global conferences + without paying duties or taxes. +

+

+ It simplifies customs procedures for the temporary movement of goods and allows goods to + enter Customs territories of the ATA Carnet system free of customs duties and taxes for up to + one year. +

+
+
\ No newline at end of file diff --git a/src/app/register/register.component.scss b/src/app/register/register.component.scss new file mode 100644 index 0000000..41be398 --- /dev/null +++ b/src/app/register/register.component.scss @@ -0,0 +1,95 @@ +/* registration.component.scss */ +.registration-container { + display: flex; + min-height: 85vh; + background-color: #f5f5f5; + + .registration-card { + flex: 1; + max-width: 500px; + padding: 2rem; + display: flex; + flex-direction: column; + justify-content: center; + background-color: white; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + + .subtitle { + text-align: center; + color: #666; + font-size: 1.5rem; + margin-bottom: 2rem; + } + + .registration-form { + display: flex; + flex-direction: column; + gap: 1.5rem; + + mat-form-field { + width: 100%; + } + + .login { + text-align: right; + margin-top: -1rem; + + .login-link { + color: #666; + text-decoration: none; + font-size: 0.875rem; + + &:hover { + text-decoration: underline; + } + } + } + + .mdc-icon-button { + margin-right: 8px; + } + + // button { + // margin-top: 1rem; + // padding: 0.5rem; + // font-size: 1rem; + // } + } + } + + .info-section { + flex: 1; + padding: 4rem; + background-color: #597b7c; + color: white; + display: flex; + flex-direction: column; + justify-content: center; + + h3 { + font-size: 2rem; + margin-bottom: 1.5rem; + } + + p { + line-height: 1.6; + font-size: 1rem; + } + } +} + +@media (max-width: 768px) { + .registration-container { + flex-direction: column; + + .registration-card { + max-width: 100%; + padding: 1.5rem; + } + + .info-section { + padding: 2rem; + display: none; + } + } +} \ No newline at end of file diff --git a/src/app/register/register.component.ts b/src/app/register/register.component.ts new file mode 100644 index 0000000..62399b1 --- /dev/null +++ b/src/app/register/register.component.ts @@ -0,0 +1,82 @@ +import { Component, inject } from '@angular/core'; +import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; +import { AuthService } from '../core/services/common/auth.service'; +import { NotificationService } from '../core/services/common/notification.service'; +import { AngularMaterialModule } from '../shared/module/angular-material.module'; +import { ApiErrorHandlerService } from '../core/services/common/api-error-handler.service'; +import { CommonModule } from '@angular/common'; +import { ThemeService } from '../core/services/theme.service'; + +@Component({ + selector: 'app-register', + imports: [AngularMaterialModule, ReactiveFormsModule, CommonModule], + templateUrl: './register.component.html', + styleUrl: './register.component.scss' +}) +export class RegisterComponent { + + registrationForm: FormGroup; + isLoading = false; + hidePassword = true; + tokenError: boolean = false; + token: string | null = null; + + private fb = inject(FormBuilder); + private authService = inject(AuthService); + private router = inject(Router); + private route = inject(ActivatedRoute); + private notificationService = inject(NotificationService); + private errorHandler = inject(ApiErrorHandlerService); + private themeService = inject(ThemeService); + + constructor() { + this.registrationForm = this.fb.group({ + email: ['', [Validators.required, Validators.email]], + password: ['', [Validators.required]] + }); + + // Get token from URL if present + this.token = this.route.snapshot.paramMap.get('token'); + + // Check if token is null or empty + if (!this.token || this.token.trim() === '') { + this.tokenError = true; + } + } + + ngOnInit(): void { + this.themeService.setTheme('default'); + } + + onSubmit(): void { + if (this.registrationForm.invalid) { + this.registrationForm.markAllAsTouched(); + return; + } + + this.isLoading = true; + const { email, password } = this.registrationForm.value; + + this.authService.register(email, password, this.token).subscribe({ + next: () => { + this.notificationService.showSuccess('Registration successful'); + this.router.navigate(['/login']); + }, + error: (error) => { + this.isLoading = false; + let errorMessage = `Registration failed. Please request a new registration link from your administrator.`; + this.notificationService.showError(errorMessage); + console.error('Registration failed:', error); + } + }); + } + + get email() { + return this.registrationForm.get('email'); + } + + get password() { + return this.registrationForm.get('password'); + } +}