holder updates
This commit is contained in:
parent
1b6ceb6be7
commit
d97afa0244
@ -7,6 +7,8 @@ export const serverRoutes: ServerRoute[] = [
|
||||
{ path: ':appId/usersettings', renderMode: RenderMode.Client },
|
||||
{ path: ':appId/add-carnet', renderMode: RenderMode.Client },
|
||||
{ path: ':appId/edit-carnet/:headerid', renderMode: RenderMode.Client },
|
||||
{ path: ':appId/add-holder', renderMode: RenderMode.Client },
|
||||
{ path: ':appId/edit-holder/:holderid', renderMode: RenderMode.Client },
|
||||
{
|
||||
path: '**',
|
||||
renderMode: RenderMode.Prerender
|
||||
|
||||
@ -15,6 +15,8 @@ export const routes: Routes = [
|
||||
{ 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: '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: '', redirectTo: 'home', pathMatch: 'full' }
|
||||
],
|
||||
canActivate: [AuthGuard, AppIdGuard]
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<mat-step *ngIf="applicationType === 'new'" [completed]="stepsCompleted.applicationDetail"
|
||||
[editable]="stepsCompleted.applicationDetail">
|
||||
<ng-template matStepLabel>Application Name</ng-template>
|
||||
<app-application [isEditMode]="isEditMode" (headerIdCreated)="onApplicationDetailCreated($event)">
|
||||
<app-application [isEditMode]="isEditMode" (applicationCreated)="onApplicationDetailCreated($event)">
|
||||
</app-application>
|
||||
</mat-step>
|
||||
|
||||
|
||||
@ -44,8 +44,10 @@ export class AddCarnetComponent {
|
||||
this.currentStep = event.selectedIndex;
|
||||
}
|
||||
|
||||
onApplicationDetailCreated(headerid: number): void {
|
||||
this.headerid = headerid;
|
||||
onApplicationDetailCreated(data: { headerid: number, applicationName: string }): void {
|
||||
// this.store.headerid.set(data.headerid);
|
||||
// this.store.applicationName.set(data.applicationName);
|
||||
this.headerid = data.headerid;
|
||||
this.stepsCompleted.applicationDetail = true;
|
||||
this.isLinear = false; // Disable linear mode after application detail is created
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ export class ApplicationComponent {
|
||||
@Input() headerid: number = 0;
|
||||
@Input() applicationName: string = '';
|
||||
|
||||
@Output() headerIdCreated = new EventEmitter<number>();
|
||||
@Output() applicationCreated = new EventEmitter<{ headerid: number, applicationName: string }>();
|
||||
|
||||
applicationDetailsForm: FormGroup;
|
||||
isLoading = false;
|
||||
@ -39,8 +39,6 @@ export class ApplicationComponent {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.applicationName = this.route.snapshot.queryParamMap.get('applicationname') || '';
|
||||
|
||||
// Patch edit form data
|
||||
if (this.applicationName) {
|
||||
this.patchFormData();
|
||||
@ -102,7 +100,7 @@ export class ApplicationComponent {
|
||||
this.applicationDetailService.createApplicationDetails(applicationDetailData).subscribe({
|
||||
next: (applicationData: any) => {
|
||||
this.notificationService.showSuccess(`Application details added successfully`);
|
||||
this.headerIdCreated.emit(+applicationData.HEADERID);
|
||||
this.applicationCreated.emit({ headerid: +applicationData.HEADERID, applicationName: applicationDetailData.name });
|
||||
this.applicationDetailsForm.get('name')?.disable();
|
||||
this.disableSaveButton = true;
|
||||
this.isLoading = false;
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<mat-step *ngIf="applicationType === 'new'" [completed]="stepsCompleted.applicationDetail"
|
||||
[editable]="stepsCompleted.applicationDetail">
|
||||
<ng-template matStepLabel>Application Name</ng-template>
|
||||
<app-application [isEditMode]="isEditMode">
|
||||
<app-application [isEditMode]="isEditMode" [applicationName]="applicationName">
|
||||
</app-application>
|
||||
</mat-step>
|
||||
|
||||
@ -15,7 +15,8 @@
|
||||
<mat-step *ngIf="applicationType === 'new'" [completed]="stepsCompleted.holderSelection"
|
||||
[editable]="stepsCompleted.applicationDetail">
|
||||
<ng-template matStepLabel>Holder Selection</ng-template>
|
||||
<app-holder (completed)="onHolderSelectionSaved($event)" [headerid]="headerid">
|
||||
<app-holder (completed)="onHolderSelectionSaved($event)" [headerid]="headerid"
|
||||
[applicationName]="applicationName">
|
||||
</app-holder>
|
||||
</mat-step>
|
||||
|
||||
|
||||
@ -26,6 +26,7 @@ export class EditCarnetComponent {
|
||||
isEditMode = true; // Set to true for edit mode
|
||||
headerid: number = 0;
|
||||
userPreferences: UserPreferences;
|
||||
applicationName: string = '';
|
||||
|
||||
// Track completion of each step
|
||||
stepsCompleted = {
|
||||
@ -45,6 +46,8 @@ export class EditCarnetComponent {
|
||||
ngOnInit(): void {
|
||||
const idParam = this.route.snapshot.paramMap.get('headerid');
|
||||
this.headerid = idParam ? parseInt(idParam, 10) : 0;
|
||||
|
||||
this.applicationName = this.route.snapshot.queryParamMap.get('applicationname') || '';
|
||||
}
|
||||
|
||||
onStepChange(event: StepperSelectionEvent): void {
|
||||
|
||||
@ -1 +1,2 @@
|
||||
<p>holder works!</p>
|
||||
<app-holder-search (holderSelectionCompleted)="onHolderSelectionSaved($event)" [applicationName]="applicationName"
|
||||
[headerid]="headerid" [selectedHolderId]="selectedHolderId"></app-holder-search>
|
||||
@ -1,12 +1,49 @@
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { Component, EventEmitter, inject, Input, Output } from '@angular/core';
|
||||
import { UserPreferences } from '../../core/models/user-preference';
|
||||
import { SearchHolderComponent } from '../../holder/search/search-holder.component';
|
||||
import { HolderService } from '../../core/services/carnet/holder.service';
|
||||
import { NotificationService } from '../../core/services/common/notification.service';
|
||||
import { Holder } from '../../core/models/carnet/holder';
|
||||
import { ApiErrorHandlerService } from '../../core/services/common/api-error-handler.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-holder',
|
||||
imports: [],
|
||||
imports: [SearchHolderComponent],
|
||||
templateUrl: './holder.component.html',
|
||||
styleUrl: './holder.component.scss'
|
||||
})
|
||||
export class HolderComponent {
|
||||
@Input() headerid: number = 0;
|
||||
@Input() applicationName: string = '';
|
||||
@Input() userPreferences: UserPreferences = {};
|
||||
@Input() isEditMode: boolean = false;
|
||||
@Output() completed = new EventEmitter<boolean>();
|
||||
|
||||
selectedHolderId: number = 0;
|
||||
|
||||
private holdersService = inject(HolderService);
|
||||
private notificationService = inject(NotificationService);
|
||||
private errorHandler = inject(ApiErrorHandlerService);
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.headerid > 0) {
|
||||
this.holdersService.getHolder(this.headerid).subscribe({
|
||||
next: (holder: Holder) => {
|
||||
if (holder) {
|
||||
this.selectedHolderId = holder.holderid;
|
||||
this.completed.emit(true);
|
||||
}
|
||||
},
|
||||
error: (error: any) => {
|
||||
let errorMessage = this.errorHandler.handleApiError(error, 'Failed to get holder details');
|
||||
this.notificationService.showError(errorMessage);
|
||||
console.error('Error getting holder details:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onHolderSelectionSaved(completed: boolean): void {
|
||||
this.completed.emit(completed);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,2 +1,15 @@
|
||||
export interface Holder {
|
||||
holderid: number;
|
||||
holderName: string;
|
||||
dbaName?: string | null;
|
||||
holderNumber: string;
|
||||
holderType: string;
|
||||
uscibMember: boolean;
|
||||
govAgency: boolean;
|
||||
address1: string;
|
||||
address2: string;
|
||||
city: string;
|
||||
state: string;
|
||||
country: string;
|
||||
zip: string;
|
||||
}
|
||||
|
||||
18
src/app/core/models/holder/basic-detail.ts
Normal file
18
src/app/core/models/holder/basic-detail.ts
Normal file
@ -0,0 +1,18 @@
|
||||
export interface BasicDetail {
|
||||
holderid?: number;
|
||||
spid: number;
|
||||
locationid: number;
|
||||
holderName: string;
|
||||
dbaName?: string | null;
|
||||
holderNumber: string;
|
||||
holderType: string;
|
||||
uscibMember: boolean;
|
||||
govAgency: boolean;
|
||||
address1: string;
|
||||
address2: string;
|
||||
city: string;
|
||||
state: string;
|
||||
country: string;
|
||||
zip: string;
|
||||
isInactive?: boolean | null;
|
||||
}
|
||||
19
src/app/core/models/holder/contact.ts
Normal file
19
src/app/core/models/holder/contact.ts
Normal file
@ -0,0 +1,19 @@
|
||||
export interface Contact {
|
||||
holdercontactid: number;
|
||||
holderid: number;
|
||||
spid: number;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
middleInitial: string | null;
|
||||
title: string;
|
||||
phone: string;
|
||||
mobile: string;
|
||||
fax?: string | null;
|
||||
email: string;
|
||||
dateCreated?: Date | null;
|
||||
createdBy?: string | null;
|
||||
lastUpdatedBy?: string | null;
|
||||
lastUpdatedDate?: Date | null;
|
||||
isInactive?: boolean | null;
|
||||
inactivatedDate?: Date | null;
|
||||
}
|
||||
3
src/app/core/models/holder/holder-filter.ts
Normal file
3
src/app/core/models/holder/holder-filter.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export interface HolderFilter {
|
||||
holderName?: string;
|
||||
}
|
||||
@ -1,9 +1,51 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
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';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class HolderService {
|
||||
private apiUrl = environment.apiUrl;
|
||||
private apiDb = environment.apiDb;
|
||||
|
||||
constructor() { }
|
||||
private http = inject(HttpClient);
|
||||
private userService = inject(UserService);
|
||||
|
||||
getHolder(id: number): Observable<Holder> {
|
||||
return this.http.get<any[]>(`${this.apiUrl}/${this.apiDb}/GetHolderstoEdit/${this.userService.getUserSpid()}/${this.userService.getUser()}/${id}`).pipe(
|
||||
filter(response => response.length > 0),
|
||||
map(response => this.mapToHolder(response?.[0])));
|
||||
}
|
||||
|
||||
private mapToHolder(holder: any): Holder {
|
||||
return {
|
||||
holderid: holder.HOLDERID,
|
||||
holderNumber: holder.HOLDERNO,
|
||||
holderType: holder.HOLDERTYPE,
|
||||
uscibMember: holder.USCIBMEMBERFLAG === 'Y',
|
||||
govAgency: holder.GOVAGENCYFLAG === 'Y',
|
||||
holderName: holder.HOLDERNAME,
|
||||
//NAMEQUALIFIER: holder.NAMEQUALIFIER || null,
|
||||
dbaName: holder.ADDLNAME || null,
|
||||
address1: holder.ADDRESS1,
|
||||
address2: holder.ADDRESS2 || null,
|
||||
city: holder.CITY,
|
||||
state: holder.STATE,
|
||||
zip: holder.ZIP,
|
||||
country: holder.COUNTRY
|
||||
};
|
||||
}
|
||||
|
||||
saveApplicationHolder(headerId: number, holderId: number) {
|
||||
const applicationHolder = {
|
||||
P_HEADERID: headerId,
|
||||
P_HOLDERID: holderId
|
||||
}
|
||||
|
||||
return this.http.patch(`${this.apiUrl}/${this.apiDb}/update-holder`, applicationHolder);
|
||||
}
|
||||
}
|
||||
|
||||
90
src/app/core/services/holder/basic-detail.service.ts
Normal file
90
src/app/core/services/holder/basic-detail.service.ts
Normal file
@ -0,0 +1,90 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { UserService } from '../common/user.service';
|
||||
import { environment } from '../../../../environments/environment';
|
||||
import { map, Observable, } from 'rxjs';
|
||||
import { BasicDetail } from '../../models/holder/basic-detail';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class BasicDetailService {
|
||||
private apiUrl = environment.apiUrl;
|
||||
private apiDb = environment.apiDb;
|
||||
|
||||
private http = inject(HttpClient);
|
||||
private userService = inject(UserService);
|
||||
|
||||
getBasicDetailByHolderId(id: number): Observable<BasicDetail> {
|
||||
return this.http.get<any[]>(`${this.apiUrl}/${this.apiDb}/GetHolderRecord/${this.userService.getUserSpid()}/${id}`).pipe(
|
||||
map(response => this.mapToBasicDetail(response)));
|
||||
}
|
||||
|
||||
private mapToBasicDetail(basicDetails: any): BasicDetail {
|
||||
return {
|
||||
holderid: basicDetails.HOLDERID,
|
||||
spid: basicDetails.SPID,
|
||||
locationid: basicDetails.LOCATIONID,
|
||||
holderNumber: basicDetails.HOLDERNO,
|
||||
holderType: basicDetails.HOLDERTYPE,
|
||||
uscibMember: basicDetails.USCIBMEMBERFLAG === 'Y',
|
||||
govAgency: basicDetails.GOVAGENCYFLAG === 'Y',
|
||||
holderName: basicDetails.HOLDERNAME,
|
||||
//NAMEQUALIFIER: basicDetails.NAMEQUALIFIER || null,
|
||||
dbaName: basicDetails.ADDLNAME || null,
|
||||
address1: basicDetails.ADDRESS1,
|
||||
address2: basicDetails.ADDRESS2 || null,
|
||||
city: basicDetails.CITY,
|
||||
state: basicDetails.STATE,
|
||||
zip: basicDetails.ZIP,
|
||||
country: basicDetails.COUNTRY
|
||||
};
|
||||
}
|
||||
|
||||
createBasicDetail(data: BasicDetail): Observable<any> {
|
||||
const basicDetail = {
|
||||
P_SPID: this.userService.getUserSpid(),
|
||||
P_CLIENTLOCATIONID: this.userService.getUserLocationid(),
|
||||
P_HOLDERNO: data.holderNumber,
|
||||
P_HOLDERTYPE: data.holderType,
|
||||
P_USCIBMEMBERFLAG: data.uscibMember ? 'Y' : 'N',
|
||||
P_GOVAGENCYFLAG: data.govAgency ? 'Y' : 'N',
|
||||
P_HOLDERNAME: data.holderName,
|
||||
P_NAMEQUALIFIER: null,
|
||||
P_ADDLNAME: data.dbaName,
|
||||
P_ADDRESS1: data.address1,
|
||||
P_ADDRESS2: data.address2,
|
||||
P_CITY: data.city,
|
||||
P_STATE: data.state,
|
||||
P_ZIP: data.zip,
|
||||
P_COUNTRY: data.country,
|
||||
P_USERID: this.userService.getUser()
|
||||
};
|
||||
|
||||
return this.http.post(`${this.apiUrl}/${this.apiDb}/CreateHolderData`, basicDetail);
|
||||
}
|
||||
|
||||
updateBasicDetails(id: number, data: BasicDetail): Observable<any> {
|
||||
const basicDetail = {
|
||||
P_HOLDERID: id,
|
||||
P_SPID: this.userService.getUserSpid(),
|
||||
P_LOCATIONID: this.userService.getUserLocationid(),
|
||||
P_HOLDERNO: data.holderNumber,
|
||||
P_HOLDERTYPE: data.holderType,
|
||||
P_USCIBMEMBERFLAG: data.uscibMember ? 'Y' : 'N',
|
||||
P_GOVAGENCYFLAG: data.govAgency ? 'Y' : 'N',
|
||||
P_HOLDERNAME: data.holderName,
|
||||
P_NAMEQUALIFIER: null,
|
||||
P_ADDLNAME: data.dbaName,
|
||||
P_ADDRESS1: data.address1,
|
||||
P_ADDRESS2: data.address2,
|
||||
P_CITY: data.city,
|
||||
P_STATE: data.state,
|
||||
P_ZIP: data.zip,
|
||||
P_COUNTRY: data.country,
|
||||
P_USERID: this.userService.getUser()
|
||||
};
|
||||
|
||||
return this.http.put(`${this.apiUrl}/${this.apiDb}/UpdateHolder`, basicDetail);
|
||||
}
|
||||
}
|
||||
90
src/app/core/services/holder/contact.service.ts
Normal file
90
src/app/core/services/holder/contact.service.ts
Normal file
@ -0,0 +1,90 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { UserService } from '../common/user.service';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { environment } from '../../../../environments/environment';
|
||||
import { map, Observable, of } from 'rxjs';
|
||||
import { Contact } from '../../models/holder/contact';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ContactService {
|
||||
private apiUrl = environment.apiUrl;
|
||||
private apiDb = environment.apiDb;
|
||||
|
||||
private http = inject(HttpClient);
|
||||
private userService = inject(UserService);
|
||||
|
||||
getContactsById(id: number): Observable<Contact[]> {
|
||||
return this.http.get<any[]>(`${this.apiUrl}/${this.apiDb}/GetHolderContacts/${this.userService.getUserSpid()}/${id}`).pipe(
|
||||
map(response => this.mapToContacts(response)));
|
||||
}
|
||||
|
||||
private mapToContacts(data: any[]): Contact[] {
|
||||
return data.map(contact => ({
|
||||
holdercontactid: contact.HOLDERCONTACTID,
|
||||
holderid: contact.HOLDERID,
|
||||
spid: contact.SPID,
|
||||
firstName: contact.FIRSTNAME,
|
||||
lastName: contact.LASTNAME,
|
||||
middleInitial: contact.MIDDLEINITIAL || null,
|
||||
title: contact.TITLE,
|
||||
phone: contact.PHONE,
|
||||
mobile: contact.MOBILE,
|
||||
fax: contact.FAX || null,
|
||||
email: contact.EMAILADDRESS,
|
||||
createdBy: contact.CREATEDBY || null,
|
||||
dateCreated: contact.DATECREATED || null,
|
||||
lastUpdatedBy: contact.LASTUPDATEDBY || null,
|
||||
lastUpdatedDate: contact.LASTUPDATEDDATE || null,
|
||||
isInactive: contact.INACTIVEFLAG === 'Y' || false,
|
||||
inactivatedDate: contact.INACTIVEDATE || null
|
||||
}));
|
||||
}
|
||||
|
||||
createContact(holderid: number, data: Contact): Observable<any> {
|
||||
const contact = {
|
||||
P_SPID: this.userService.getUserSpid(),
|
||||
P_HOLDERID: holderid,
|
||||
P_CONTACTSTABLE: [{
|
||||
P_FIRSTNAME: data.firstName,
|
||||
P_LASTNAME: data.lastName,
|
||||
P_MIDDLEINITIAL: data.middleInitial,
|
||||
P_TITLE: data.title,
|
||||
P_EMAILADDRESS: data.email,
|
||||
P_MOBILENO: data.mobile,
|
||||
P_PHONENO: data.phone,
|
||||
P_FAXNO: data.fax
|
||||
}],
|
||||
P_USERID: this.userService.getUser()
|
||||
}
|
||||
|
||||
return this.http.post(`${this.apiUrl}/${this.apiDb}/CreateHoldercontact`, contact);
|
||||
}
|
||||
|
||||
updateContact(holdercontactid: number, data: Contact): Observable<any> {
|
||||
const contact = {
|
||||
P_HOLDERCONTACTID: holdercontactid,
|
||||
P_SPID: this.userService.getUserSpid(),
|
||||
P_FIRSTNAME: data.firstName,
|
||||
P_LASTNAME: data.lastName,
|
||||
P_MIDDLEINITIAL: data.middleInitial,
|
||||
P_TITLE: data.title,
|
||||
P_EMAILADDRESS: data.email,
|
||||
P_MOBILENO: data.mobile,
|
||||
P_PHONENO: data.phone,
|
||||
P_FAXNO: data.fax,
|
||||
P_USERID: this.userService.getUser()
|
||||
}
|
||||
|
||||
return this.http.put(`${this.apiUrl}/${this.apiDb}/UpdateHolderContact`, contact);
|
||||
}
|
||||
|
||||
inactivateHolderContact(holdercontactid: number) {
|
||||
return this.http.patch(`${this.apiUrl}/${this.apiDb}/InactivateHolderContact/${this.userService.getUserSpid()}/${holdercontactid}/${this.userService.getSafeUser()}`, {});
|
||||
}
|
||||
|
||||
reactivateHolderContact(holdercontactid: number) {
|
||||
return this.http.patch(`${this.apiUrl}/${this.apiDb}/ReactivateHolderContact/${this.userService.getUserSpid()}/${holdercontactid}/${this.userService.getSafeUser()}`, {});
|
||||
}
|
||||
}
|
||||
54
src/app/core/services/holder/holder.service.ts
Normal file
54
src/app/core/services/holder/holder.service.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { environment } from '../../../../environments/environment';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { UserService } from '../common/user.service';
|
||||
import { map, Observable } from 'rxjs';
|
||||
import { BasicDetail } from '../../models/holder/basic-detail';
|
||||
import { HolderFilter } from '../../models/holder/holder-filter';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class HolderService {
|
||||
private apiUrl = environment.apiUrl;
|
||||
private apiDb = environment.apiDb;
|
||||
|
||||
private http = inject(HttpClient);
|
||||
private userService = inject(UserService);
|
||||
|
||||
getHolders(filter: HolderFilter): Observable<BasicDetail[]> {
|
||||
return this.http.get<any[]>(`${this.apiUrl}/${this.apiDb}/SearchHolder/${this.userService.getUserSpid()}?P_HOLDERNAME=${filter.holderName}`).pipe(
|
||||
map(response => this.mapToHolders(response))
|
||||
)
|
||||
}
|
||||
|
||||
inactivateHolder(holderid: number) {
|
||||
return this.http.patch(`${this.apiUrl}/${this.apiDb}/InactivateHolder/${this.userService.getUserSpid()}/${holderid}/${this.userService.getSafeUser()}`, {});
|
||||
}
|
||||
|
||||
reactivateHolder(holderid: number) {
|
||||
return this.http.patch(`${this.apiUrl}/${this.apiDb}/ReactivateHolder/${this.userService.getUserSpid()}/${holderid}/${this.userService.getSafeUser()}`, {});
|
||||
}
|
||||
|
||||
private mapToHolders(data: any[]): BasicDetail[] {
|
||||
return data.map((holderDetail) => ({
|
||||
holderid: holderDetail.HOLDERID,
|
||||
spid: holderDetail.SPID,
|
||||
locationid: holderDetail.LOCATIONID,
|
||||
holderNumber: holderDetail.HOLDERNO,
|
||||
holderType: holderDetail.HOLDERTYPE,
|
||||
uscibMember: holderDetail.USCIBMEMBERFLAG === 'Y',
|
||||
govAgency: holderDetail.GOVAGENCYFLAG === 'Y',
|
||||
holderName: holderDetail.HOLDERNAME,
|
||||
//: holderDetail.NAMEQUALIFIER,
|
||||
dbaName: holderDetail.ADDLNAME,
|
||||
address1: holderDetail.ADDRESS1,
|
||||
address2: holderDetail.ADDRESS2,
|
||||
city: holderDetail.CITY,
|
||||
state: holderDetail.STATE,
|
||||
zip: holderDetail.ZIP,
|
||||
country: holderDetail.COUNTRY,
|
||||
isInactive: holderDetail.INACTIVEFLAG === 'Y'
|
||||
}))
|
||||
}
|
||||
}
|
||||
23
src/app/holder/add/add-holder.component.html
Normal file
23
src/app/holder/add/add-holder.component.html
Normal file
@ -0,0 +1,23 @@
|
||||
<!-- <button mat-stroked-button color="accent" class="back-btn" (click)="goToEditCarnet()"
|
||||
[hidden]="!isEditMode">Back</button> -->
|
||||
|
||||
<div class="client-carnet-container">
|
||||
<!-- Stepper Section (shown after questions are answered) -->
|
||||
<mat-stepper orientation="vertical" [linear]="isLinear" (selectionChange)="onStepChange($event)"
|
||||
[selectedIndex]="currentStep">
|
||||
|
||||
<!-- Holder Detail Step -->
|
||||
<mat-step [completed]="stepsCompleted.holderDetails" [editable]="stepsCompleted.holderDetails">
|
||||
<ng-template matStepLabel>Holder Details</ng-template>
|
||||
<app-basic-detail [isEditMode]="isEditMode" (holderIdCreated)="onHolderIdCreated($event)">
|
||||
</app-basic-detail>
|
||||
</mat-step>
|
||||
|
||||
<!-- Holder Contact Step -->
|
||||
<mat-step [completed]="stepsCompleted.holderContacts" [editable]="stepsCompleted.holderContacts">
|
||||
<ng-template matStepLabel>Holder Contacts</ng-template>
|
||||
<app-contacts [holderid]="holderid" [isEditMode]="isEditMode"
|
||||
[userPreferences]="userPreferences"></app-contacts>
|
||||
</mat-step>
|
||||
</mat-stepper>
|
||||
</div>
|
||||
4
src/app/holder/add/add-holder.component.scss
Normal file
4
src/app/holder/add/add-holder.component.scss
Normal file
@ -0,0 +1,4 @@
|
||||
// .back-btn{
|
||||
// padding: 0 10px !important;
|
||||
// margin-bottom: 10px !important;
|
||||
// }
|
||||
66
src/app/holder/add/add-holder.component.ts
Normal file
66
src/app/holder/add/add-holder.component.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { AngularMaterialModule } from '../../shared/module/angular-material.module';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { StepperSelectionEvent } from '@angular/cdk/stepper';
|
||||
import { BasicDetailComponent } from '../basic-details/basic-details.component';
|
||||
import { MatStepperModule } from '@angular/material/stepper';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { ContactsComponent } from '../contacts/contacts.component';
|
||||
import { UserPreferences } from '../../core/models/user-preference';
|
||||
import { UserPreferencesService } from '../../core/services/user-preference.service';
|
||||
import { NavigationService } from '../../core/services/common/navigation.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-add-holder',
|
||||
imports: [
|
||||
AngularMaterialModule,
|
||||
CommonModule,
|
||||
ReactiveFormsModule,
|
||||
BasicDetailComponent,
|
||||
MatStepperModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
ContactsComponent
|
||||
],
|
||||
templateUrl: './add-holder.component.html',
|
||||
styleUrl: './add-holder.component.scss'
|
||||
})
|
||||
export class AddHolderComponent {
|
||||
currentStep = 0;
|
||||
isLinear = true;
|
||||
isEditMode = false;
|
||||
holderid: number = 0;
|
||||
userPreferences: UserPreferences;
|
||||
|
||||
stepsCompleted = {
|
||||
holderDetails: false,
|
||||
holderContacts: false
|
||||
};
|
||||
|
||||
private navigationService = inject(NavigationService);
|
||||
|
||||
constructor(
|
||||
userPrefenceService: UserPreferencesService
|
||||
) {
|
||||
this.userPreferences = userPrefenceService.getPreferences();
|
||||
}
|
||||
|
||||
onStepChange(event: StepperSelectionEvent): void {
|
||||
this.currentStep = event.selectedIndex;
|
||||
}
|
||||
|
||||
onHolderIdCreated(holderid: number): void {
|
||||
this.holderid = holderid;
|
||||
this.stepsCompleted.holderDetails = true;
|
||||
}
|
||||
|
||||
// goToEditCarnet(): void {
|
||||
// this.navigationService.navigate(["edit-carnet", this.store.headerid()],
|
||||
// {
|
||||
// state: { isEditMode: true },
|
||||
// queryParams: { applicationname: this.store.applicationName() }
|
||||
// })
|
||||
// }
|
||||
}
|
||||
158
src/app/holder/basic-details/basic-details.component.html
Normal file
158
src/app/holder/basic-details/basic-details.component.html
Normal file
@ -0,0 +1,158 @@
|
||||
<div class="basic-details-container">
|
||||
<mat-card class="details-card mat-elevation-z4">
|
||||
<mat-card-content>
|
||||
<div class="loading-shade" *ngIf="isLoading">
|
||||
<mat-spinner diameter="50"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<form [formGroup]="basicDetailsForm" class="details-form" (ngSubmit)="saveBasicDetails()">
|
||||
|
||||
<!-- Holder Information -->
|
||||
<div class="form-row">
|
||||
<mat-form-field appearance="outline" class="name">
|
||||
<mat-label>Holder Name</mat-label>
|
||||
<input matInput formControlName="holderName" required>
|
||||
<mat-error *ngIf="f['holderName'].errors?.['required']">
|
||||
Name is required
|
||||
</mat-error>
|
||||
<mat-error *ngIf="f['holderName'].errors?.['maxlength']">
|
||||
Maximum 100 characters allowed
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="lookup-code">
|
||||
<mat-label>DBA Name</mat-label>
|
||||
<input matInput formControlName="dbaName">
|
||||
<mat-error *ngIf="f['dbaName'].errors?.['required']">
|
||||
DBA name is required
|
||||
</mat-error>
|
||||
<mat-error *ngIf="f['dbaName'].errors?.['maxlength']">
|
||||
Maximum 20 characters allowed
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<mat-form-field appearance="outline" class="holder-number">
|
||||
<mat-label>Tax ID No</mat-label>
|
||||
<input matInput formControlName="holderNumber" required>
|
||||
<mat-error *ngIf="f['holderNumber'].errors?.['required']">
|
||||
Tax ID No is required
|
||||
</mat-error>
|
||||
<mat-error *ngIf="f['holderNumber'].errors?.['maxlength']">
|
||||
Maximum 20 characters allowed
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<mat-radio-group formControlName="holderType" class="radio-group">
|
||||
<mat-label>Holder Type </mat-label>
|
||||
<mat-radio-button value="CORP">Corporation</mat-radio-button>
|
||||
<mat-radio-button value="IND">Individual</mat-radio-button>
|
||||
<mat-radio-button value="GOV ">Government Agency </mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<mat-radio-group formControlName="uscibMember" class="radio-group">
|
||||
<mat-label>Are you a member of USCIB ?</mat-label>
|
||||
<mat-radio-button [value]="true">Yes</mat-radio-button>
|
||||
<mat-radio-button [value]="false">No</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<mat-radio-group formControlName="govAgency" class="radio-group">
|
||||
<mat-label>Are you belong to Government Agency ?</mat-label>
|
||||
<mat-radio-button [value]="true">Yes</mat-radio-button>
|
||||
<mat-radio-button [value]="false">No</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</div>
|
||||
|
||||
<!-- Address Information -->
|
||||
<div class="form-row">
|
||||
<mat-form-field appearance="outline" class="address1">
|
||||
<mat-label>Address Line 1</mat-label>
|
||||
<input matInput formControlName="address1" required>
|
||||
<mat-error *ngIf="f['address1'].errors?.['required']">
|
||||
Address is required
|
||||
</mat-error>
|
||||
<mat-error *ngIf="f['address1'].errors?.['maxlength']">
|
||||
Maximum 100 characters allowed
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<mat-form-field appearance="outline" class="address2">
|
||||
<mat-label>Address Line 2 (Optional)</mat-label>
|
||||
<input matInput formControlName="address2">
|
||||
<mat-error *ngIf="f['address2'].errors?.['maxlength']">
|
||||
Maximum 100 characters allowed
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<!-- Location Information -->
|
||||
<div class="form-row">
|
||||
<mat-form-field appearance="outline" class="city">
|
||||
<mat-label>City</mat-label>
|
||||
<input matInput formControlName="city" required>
|
||||
<mat-error *ngIf="f['city'].errors?.['required']">
|
||||
City is required
|
||||
</mat-error>
|
||||
<mat-error *ngIf="f['city'].errors?.['maxlength']">
|
||||
Maximum 50 characters allowed
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="country">
|
||||
<mat-label>Country</mat-label>
|
||||
<mat-select formControlName="country" required
|
||||
(selectionChange)="onCountryChange($event.value)">
|
||||
<mat-option *ngFor="let country of countries" [value]="country.value">
|
||||
{{ country.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="f['country'].errors?.['required']">
|
||||
Country is required
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="state">
|
||||
<mat-label>State/Province</mat-label>
|
||||
<mat-select formControlName="state" required>
|
||||
<mat-option *ngFor="let state of states" [value]="state.value">
|
||||
{{ state.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="f['state'].errors?.['required']">
|
||||
State is required
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="zip">
|
||||
<mat-label>ZIP/Postal Code</mat-label>
|
||||
<input matInput formControlName="zip" required>
|
||||
<mat-error *ngIf="f['zip'].errors?.['required']">
|
||||
ZIP/Postal code is required
|
||||
</mat-error>
|
||||
<mat-error
|
||||
*ngIf="f['country']?.value === 'US' && f['zip']?.touched && f['zip']?.errors?.['invalidUSZip']">
|
||||
Please enter a valid 5-digit US ZIP code
|
||||
</mat-error>
|
||||
<mat-error
|
||||
*ngIf="f['country']?.value === 'CA' && f['zip']?.touched && f['zip']?.errors?.['invalidCanadaPostal']">
|
||||
Please enter a valid postal code (e.g., A1B2C3)
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button mat-raised-button color="primary" type="submit"
|
||||
[disabled]="basicDetailsForm.invalid || !basicDetailsForm.dirty">
|
||||
{{ isEditMode ? 'Update' : 'Save' }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
109
src/app/holder/basic-details/basic-details.component.scss
Normal file
109
src/app/holder/basic-details/basic-details.component.scss
Normal file
@ -0,0 +1,109 @@
|
||||
.basic-details-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
width: 100%;
|
||||
|
||||
.details-card {
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
|
||||
.loading-shade {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.details-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 16px;
|
||||
align-items: start;
|
||||
|
||||
.name {
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
.lookup-code {
|
||||
grid-column: span 1;
|
||||
}
|
||||
|
||||
.holder-number {
|
||||
grid-column: span 1;
|
||||
}
|
||||
|
||||
.address1,
|
||||
.address2 {
|
||||
grid-column: span 3;
|
||||
}
|
||||
|
||||
.city,
|
||||
.state,
|
||||
.country,
|
||||
.zip {
|
||||
grid-column: span 1;
|
||||
}
|
||||
|
||||
mat-radio-group {
|
||||
mat-label {
|
||||
color: var(--mat-sys-on-surface);
|
||||
font-family: var(--mat-sys-body-medium-font);
|
||||
line-height: var(--mat-sys-body-medium-line-height);
|
||||
font-size: var(--mat-sys-body-medium-size);
|
||||
letter-spacing: var(--mat-sys-body-medium-tracking);
|
||||
font-weight: var(--mat-sys-medium-font-weight);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
mat-form-field {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.basic-details-container {
|
||||
.details-card {
|
||||
.form-row {
|
||||
grid-template-columns: 1fr;
|
||||
|
||||
.name,
|
||||
.lookup-code,
|
||||
.address1,
|
||||
.address2,
|
||||
.city,
|
||||
.state,
|
||||
.zip,
|
||||
.country {
|
||||
grid-column: span 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
204
src/app/holder/basic-details/basic-details.component.ts
Normal file
204
src/app/holder/basic-details/basic-details.component.ts
Normal file
@ -0,0 +1,204 @@
|
||||
import { Component, EventEmitter, inject, Input, OnDestroy, OnInit, Output } from '@angular/core';
|
||||
import { AngularMaterialModule } from '../../shared/module/angular-material.module';
|
||||
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { State } from '../../core/models/state';
|
||||
import { Region } from '../../core/models/region';
|
||||
import { NotificationService } from '../../core/services/common/notification.service';
|
||||
import { ApiErrorHandlerService } from '../../core/services/common/api-error-handler.service';
|
||||
import { ZipCodeValidator } from '../../shared/validators/zipcode-validator';
|
||||
import { CommonService } from '../../core/services/common/common.service';
|
||||
import { Subject, takeUntil } from 'rxjs';
|
||||
import { Country } from '../../core/models/country';
|
||||
import { BasicDetailService } from '../../core/services/holder/basic-detail.service';
|
||||
import { BasicDetail } from '../../core/models/holder/basic-detail';
|
||||
|
||||
@Component({
|
||||
selector: 'app-basic-detail',
|
||||
imports: [AngularMaterialModule, ReactiveFormsModule, CommonModule],
|
||||
templateUrl: './basic-details.component.html',
|
||||
styleUrl: './basic-details.component.scss'
|
||||
})
|
||||
export class BasicDetailComponent implements OnInit, OnDestroy {
|
||||
|
||||
@Input() isEditMode = false;
|
||||
@Input() holderid: number = 0;
|
||||
|
||||
@Output() holderIdCreated = new EventEmitter<number>();
|
||||
@Output() holderName = new EventEmitter<string>();
|
||||
|
||||
basicDetailsForm: FormGroup;
|
||||
countries: Country[] = [];
|
||||
regions: Region[] = [];
|
||||
states: State[] = [];
|
||||
|
||||
isLoading = false;
|
||||
countriesHasStates = ['US', 'CA', 'MX'];
|
||||
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
private fb = inject(FormBuilder);
|
||||
private basicDetailService = inject(BasicDetailService);
|
||||
private notificationService = inject(NotificationService);
|
||||
private commonService = inject(CommonService);
|
||||
private errorHandler = inject(ApiErrorHandlerService);
|
||||
|
||||
constructor() {
|
||||
this.basicDetailsForm = this.createForm();
|
||||
}
|
||||
|
||||
createForm(): FormGroup {
|
||||
return this.fb.group({
|
||||
holderName: ['', [Validators.required, Validators.maxLength(100)]],
|
||||
dbaName: ['', [Validators.maxLength(20)]],
|
||||
holderNumber: ['', [Validators.required, Validators.maxLength(20)]],
|
||||
holderType: ['', [Validators.required, Validators.maxLength(20)]],
|
||||
uscibMember: [false, [Validators.required]],
|
||||
govAgency: [false, [Validators.required]],
|
||||
address1: ['', [Validators.required, Validators.maxLength(100)]],
|
||||
address2: ['', Validators.maxLength(100)],
|
||||
city: ['', [Validators.required, Validators.maxLength(50)]],
|
||||
state: ['', Validators.required],
|
||||
country: ['', Validators.required],
|
||||
zip: ['', [Validators.required, ZipCodeValidator('country')]]
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadLookupData();
|
||||
|
||||
if (this.holderid > 0) {
|
||||
this.isLoading = true;
|
||||
this.basicDetailService.getBasicDetailByHolderId(this.holderid).subscribe({
|
||||
next: (basicDetail: BasicDetail) => {
|
||||
this.patchFormData(basicDetail);
|
||||
this.holderName.emit(basicDetail.holderName);
|
||||
this.isLoading = false;
|
||||
},
|
||||
error: (error: any) => {
|
||||
let errorMessage = this.errorHandler.handleApiError(error, 'Failed to load basic details');
|
||||
this.notificationService.showError(errorMessage);
|
||||
this.isLoading = false;
|
||||
console.error('Error loading basic details:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
|
||||
onCountryChange(country: string): void {
|
||||
this.basicDetailsForm.get('state')?.reset();
|
||||
|
||||
if (country) {
|
||||
this.loadStates(country);
|
||||
}
|
||||
|
||||
this.basicDetailsForm.get('zip')?.updateValueAndValidity();
|
||||
}
|
||||
|
||||
saveBasicDetails() {
|
||||
if (this.basicDetailsForm.invalid) {
|
||||
this.basicDetailsForm.markAllAsTouched();
|
||||
return;
|
||||
}
|
||||
|
||||
const basicDetailData: BasicDetail = this.basicDetailsForm.value;
|
||||
|
||||
const saveObservable = this.isEditMode && this.holderid > 0
|
||||
? this.basicDetailService.updateBasicDetails(this.holderid, basicDetailData)
|
||||
: this.basicDetailService.createBasicDetail(basicDetailData);
|
||||
|
||||
saveObservable.subscribe({
|
||||
next: (basicData: any) => {
|
||||
this.notificationService.showSuccess(`Basic details ${this.isEditMode ? 'updated' : 'added'} successfully`);
|
||||
|
||||
if (!this.isEditMode) {
|
||||
this.holderIdCreated.emit(basicData.HOLDERID);
|
||||
}
|
||||
|
||||
if (this.isEditMode) {
|
||||
this.holderName.emit(basicDetailData.holderName);
|
||||
}
|
||||
},
|
||||
error: (error: any) => {
|
||||
let errorMessage = this.errorHandler.handleApiError(error, `Failed to ${this.isEditMode ? 'update' : 'add'} basic details`);
|
||||
this.notificationService.showError(errorMessage);
|
||||
console.error('Error saving basic details:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateStateControl(controlName: string, country: string): void {
|
||||
const stateControl = this.basicDetailsForm.get(controlName);
|
||||
if (this.countriesHasStates.includes(country)) {
|
||||
stateControl?.enable();
|
||||
} else {
|
||||
stateControl?.disable();
|
||||
stateControl?.setValue('FN');
|
||||
}
|
||||
}
|
||||
|
||||
patchFormData(data: BasicDetail): void {
|
||||
|
||||
this.basicDetailsForm.patchValue({
|
||||
holderName: data.holderName,
|
||||
dbaName: data.dbaName,
|
||||
holderNumber: data.holderNumber,
|
||||
holderType: data.holderType,
|
||||
uscibMember: data.uscibMember,
|
||||
govAgency: data.govAgency,
|
||||
address1: data.address1,
|
||||
address2: data.address2,
|
||||
city: data.city,
|
||||
state: data.state,
|
||||
country: data.country,
|
||||
zip: data.zip
|
||||
})
|
||||
|
||||
if (data.country) {
|
||||
this.loadStates(data.country);
|
||||
}
|
||||
}
|
||||
|
||||
get f() {
|
||||
return this.basicDetailsForm.controls;
|
||||
}
|
||||
|
||||
loadLookupData(): void {
|
||||
|
||||
this.commonService.getCountries(0)
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe({
|
||||
next: (countries) => {
|
||||
this.countries = countries;
|
||||
this.isLoading = false;
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Failed to load countries', error);
|
||||
this.isLoading = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
loadStates(country: string): void {
|
||||
this.isLoading = true;
|
||||
country = this.countriesHasStates.includes(country) ? country : 'FN';
|
||||
this.commonService.getStates(country, this.holderid)
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe({
|
||||
next: (states) => {
|
||||
this.states = states;
|
||||
this.updateStateControl('state', country);
|
||||
this.isLoading = false;
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Failed to load states', error);
|
||||
this.isLoading = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
260
src/app/holder/contacts/contacts.component.html
Normal file
260
src/app/holder/contacts/contacts.component.html
Normal file
@ -0,0 +1,260 @@
|
||||
<div class="contacts-container">
|
||||
<div class="actions-bar">
|
||||
<mat-slide-toggle (change)="toggleShowInactiveContacts()">
|
||||
Show Inactive Contacts
|
||||
</mat-slide-toggle>
|
||||
|
||||
<button mat-raised-button color="primary" (click)="addNewContact()">
|
||||
<mat-icon>add</mat-icon> Add New Contact
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="table-container mat-elevation-z8">
|
||||
<div class="loading-shade" *ngIf="isLoading">
|
||||
<mat-spinner diameter="50"></mat-spinner>
|
||||
</div>
|
||||
<table mat-table [dataSource]="dataSource" matSort>
|
||||
<!-- First Name Column -->
|
||||
<ng-container matColumnDef="firstName">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>First Name</th>
|
||||
<td mat-cell *matCellDef="let contact">{{ contact.firstName }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Last Name Column -->
|
||||
<ng-container matColumnDef="lastName">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>Last Name</th>
|
||||
<td mat-cell *matCellDef="let contact">{{ contact.lastName }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Title Column -->
|
||||
<ng-container matColumnDef="title">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>Title</th>
|
||||
<td mat-cell *matCellDef="let contact">{{ contact.title }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Phone Column -->
|
||||
<ng-container matColumnDef="phone">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>Phone</th>
|
||||
<td mat-cell *matCellDef="let contact">{{ contact.phone | phone }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Mobile Column -->
|
||||
<ng-container matColumnDef="mobile">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>Mobile</th>
|
||||
<td mat-cell *matCellDef="let contact">{{ contact.mobile | phone }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Email Column -->
|
||||
<ng-container matColumnDef="email">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>Email</th>
|
||||
<td mat-cell *matCellDef="let contact">{{ contact.email }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Default Contact Column -->
|
||||
<!-- <ng-container matColumnDef="defaultContact">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>Default</th>
|
||||
<td mat-cell *matCellDef="let contact">
|
||||
<mat-icon [color]="contact.defaultContact ? 'primary' : ''">
|
||||
{{ contact.defaultContact ? 'star' : 'star_border' }}
|
||||
</mat-icon>
|
||||
</td>
|
||||
</ng-container> -->
|
||||
|
||||
<!-- Actions Column -->
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef>Actions</th>
|
||||
<td mat-cell *matCellDef="let contact">
|
||||
<div>
|
||||
<button mat-icon-button color="primary" (click)="editContact(contact)" matTooltip="Edit">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button color="warn" *ngIf="!contact.isInactive"
|
||||
(click)="inactivateContact(contact.holdercontactid)" matTooltip="Inactivate">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button color="warn" *ngIf="contact.isInactive"
|
||||
(click)="reactivateContact(contact.holdercontactid)" matTooltip="Reactivate">
|
||||
<mat-icon>loupe</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<!-- <button mat-icon-button (click)="setDefaultContact(contact.contactId)"
|
||||
[color]="contact.defaultContact ? 'primary' : ''" matTooltip="Set as default">
|
||||
<mat-icon>star</mat-icon>
|
||||
</button> -->
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
|
||||
<tr matNoDataRow *matNoDataRow>
|
||||
<td [colSpan]="displayedColumns.length" class="no-data-message">
|
||||
<mat-icon>info</mat-icon>
|
||||
<span>No records available</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator [length]="dataSource.data.length" [pageSizeOptions]="[userPreferences.pageSize || 1]"
|
||||
[hidePageSize]="true" showFirstLastButtons></mat-paginator>
|
||||
</div>
|
||||
|
||||
<!-- Contact Form -->
|
||||
<div class="form-container" *ngIf="showForm">
|
||||
<form [formGroup]="contactForm" (ngSubmit)="saveContact()">
|
||||
<div class="form-header">
|
||||
<h3>{{ isEditing ? 'Edit Contact' : 'Add New Contact' }}</h3>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>First Name</mat-label>
|
||||
<input matInput formControlName="firstName" required>
|
||||
<mat-icon matSuffix>person</mat-icon>
|
||||
<mat-error *ngIf="contactForm.get('firstName')?.errors?.['required']">
|
||||
First name is required
|
||||
</mat-error>
|
||||
<mat-error *ngIf="contactForm.get('firstName')?.errors?.['maxlength']">
|
||||
Maximum 50 characters allowed
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="small-field">
|
||||
<mat-label>Middle Initial</mat-label>
|
||||
<input matInput formControlName="middleInitial" maxlength="1">
|
||||
<mat-error *ngIf="contactForm.get('middleInitial')?.errors?.['maxlength']">
|
||||
Only 1 character allowed
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Last Name</mat-label>
|
||||
<input matInput formControlName="lastName" required>
|
||||
<mat-icon matSuffix>person</mat-icon>
|
||||
<mat-error *ngIf="contactForm.get('lastName')?.errors?.['required']">
|
||||
Last name is required
|
||||
</mat-error>
|
||||
<mat-error *ngIf="contactForm.get('lastName')?.errors?.['maxlength']">
|
||||
Maximum 50 characters allowed
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Title</mat-label>
|
||||
<input matInput formControlName="title" required>
|
||||
<mat-icon matSuffix>work</mat-icon>
|
||||
<mat-error *ngIf="contactForm.get('title')?.errors?.['required']">
|
||||
Title is required
|
||||
</mat-error>
|
||||
<mat-error *ngIf="contactForm.get('title')?.errors?.['maxlength']">
|
||||
Maximum 100 characters allowed
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Phone</mat-label>
|
||||
<input matInput formControlName="phone" required>
|
||||
<mat-icon matSuffix>phone</mat-icon>
|
||||
<mat-error *ngIf="contactForm.get('phone')?.errors?.['required']">
|
||||
Phone is required
|
||||
</mat-error>
|
||||
<mat-error *ngIf="contactForm.get('phone')?.errors?.['pattern']">
|
||||
Please enter a valid phone number (10-15 digits)
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Mobile</mat-label>
|
||||
<input matInput formControlName="mobile">
|
||||
<mat-icon matSuffix>smartphone</mat-icon>
|
||||
<mat-error *ngIf="contactForm.get('mobile')?.errors?.['required']">
|
||||
Mobile is required
|
||||
</mat-error>
|
||||
<mat-error *ngIf="contactForm.get('mobile')?.errors?.['pattern']">
|
||||
Please enter a valid mobile number (10-15 digits)
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Fax</mat-label>
|
||||
<input matInput formControlName="fax">
|
||||
<mat-icon matSuffix>fax</mat-icon>
|
||||
<mat-error *ngIf="contactForm.get('fax')?.errors?.['pattern']">
|
||||
Please enter a valid fax number (10-15 digits)
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Email</mat-label>
|
||||
<input matInput formControlName="email" required>
|
||||
<mat-icon matSuffix>email</mat-icon>
|
||||
<mat-error *ngIf="contactForm.get('email')?.errors?.['required']">
|
||||
Email is required
|
||||
</mat-error>
|
||||
<mat-error *ngIf="contactForm.get('email')?.errors?.['email']">
|
||||
Please enter a valid email address
|
||||
</mat-error>
|
||||
<mat-error *ngIf="contactForm.get('email')?.errors?.['maxlength']">
|
||||
Maximum 100 characters allowed
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<!--
|
||||
<div class="form-row">
|
||||
<mat-checkbox formControlName="defaultContact">Default Contact</mat-checkbox>
|
||||
</div> -->
|
||||
<div *ngIf="isEditing" class="readonly-section">
|
||||
<div class="readonly-fields">
|
||||
<div class="field-column">
|
||||
<!-- Last Changed By -->
|
||||
<div class="readonly-field">
|
||||
<label>Last Changed By</label>
|
||||
<div class="readonly-value">
|
||||
{{contactReadOnlyFields.lastChangedBy || 'N/A'}}
|
||||
</div>
|
||||
</div>
|
||||
<!-- Inactive status -->
|
||||
<div class="readonly-field">
|
||||
<label>Inactive Status </label>
|
||||
<div class="readonly-value">
|
||||
{{contactReadOnlyFields.isInactive === true ? 'Yes' : 'No' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field-column">
|
||||
|
||||
<!-- Last Changed Date -->
|
||||
<div class="readonly-field">
|
||||
<label>Last Changed Date</label>
|
||||
<div class="readonly-value">
|
||||
{{(contactReadOnlyFields.lastChangedDate | date:'mediumDate':'UTC') || 'N/A'}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Inactivated Date -->
|
||||
<div class="readonly-field">
|
||||
<label>Inactivated Date</label>
|
||||
<div class="readonly-value">
|
||||
{{(contactReadOnlyFields.inactivatedDate | date:'mediumDate':'UTC') || 'N/A'}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button mat-button type="button" (click)="cancelEdit()">Cancel</button>
|
||||
<button mat-raised-button color="primary" type="submit" [disabled]="contactForm.invalid">
|
||||
{{ isEditing ? 'Update' : 'Save' }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
181
src/app/holder/contacts/contacts.component.scss
Normal file
181
src/app/holder/contacts/contacts.component.scss
Normal file
@ -0,0 +1,181 @@
|
||||
.contacts-container {
|
||||
padding: 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
|
||||
.actions-bar {
|
||||
clear: both;
|
||||
margin-bottom: -16px;
|
||||
|
||||
mat-slide-toggle {
|
||||
transform: scale(0.8);
|
||||
margin-left: -0.5rem;
|
||||
}
|
||||
|
||||
button {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
.table-container {
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
border-radius: 8px;
|
||||
|
||||
.loading-shade {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
mat-table {
|
||||
width: 100%;
|
||||
|
||||
mat-icon {
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
|
||||
.mat-column-actions {
|
||||
width: 180px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mat-column-defaultContact {
|
||||
width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.no-data-message {
|
||||
text-align: center;
|
||||
padding: 0.9rem;
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
|
||||
mat-icon {
|
||||
font-size: 1rem;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
margin-bottom: -3px;
|
||||
}
|
||||
}
|
||||
|
||||
mat-paginator {
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.12);
|
||||
border-radius: 0 0 8px 8px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.form-container {
|
||||
background-color: white;
|
||||
padding: 24px;
|
||||
border-radius: 8px;
|
||||
margin-top: 16px;
|
||||
|
||||
.form-header {
|
||||
margin-bottom: 24px;
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
color: var(--mat-sys-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
|
||||
.form-row {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
|
||||
mat-form-field {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.small-field {
|
||||
max-width: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 16px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.top-divider {
|
||||
padding-top: 0.5rem;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
.bottom-divider {
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.readonly-section {
|
||||
|
||||
.readonly-fields {
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
|
||||
.field-column {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.readonly-field {
|
||||
label {
|
||||
display: block;
|
||||
font-size: 0.875rem;
|
||||
color: #666;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.readonly-value {
|
||||
padding: 0.25rem;
|
||||
font-size: 0.9375rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Responsive adjustments
|
||||
@media (max-width: 768px) {
|
||||
.contacts-container {
|
||||
padding: 16px;
|
||||
|
||||
.form-row {
|
||||
flex-direction: column;
|
||||
gap: 16px !important;
|
||||
|
||||
.small-field {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
232
src/app/holder/contacts/contacts.component.ts
Normal file
232
src/app/holder/contacts/contacts.component.ts
Normal file
@ -0,0 +1,232 @@
|
||||
import { Component, EventEmitter, inject, Input, Output, ViewChild } from '@angular/core';
|
||||
import { AngularMaterialModule } from '../../shared/module/angular-material.module';
|
||||
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||
import { PhonePipe } from '../../shared/pipes/phone.pipe';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MatPaginator, MatPaginatorIntl } from '@angular/material/paginator';
|
||||
import { MatSort } from '@angular/material/sort';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { UserPreferences } from '../../core/models/user-preference';
|
||||
import { NotificationService } from '../../core/services/common/notification.service';
|
||||
import { ApiErrorHandlerService } from '../../core/services/common/api-error-handler.service';
|
||||
import { ContactService } from '../../core/services/holder/contact.service';
|
||||
import { Contact } from '../../core/models/holder/contact';
|
||||
import { CustomPaginator } from '../../shared/custom-paginator';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { ConfirmDialogComponent } from '../../shared/components/confirm-dialog/confirm-dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-contacts',
|
||||
imports: [AngularMaterialModule, ReactiveFormsModule, PhonePipe, CommonModule],
|
||||
templateUrl: './contacts.component.html',
|
||||
styleUrl: './contacts.component.scss',
|
||||
providers: [{ provide: MatPaginatorIntl, useClass: CustomPaginator }],
|
||||
})
|
||||
export class ContactsComponent {
|
||||
@Input() isEditMode: boolean = false;
|
||||
@Input() holderid: number = 0;
|
||||
@Input() userPreferences: UserPreferences = {};
|
||||
|
||||
@Output() hasContacts = new EventEmitter<boolean>();
|
||||
|
||||
@ViewChild(MatPaginator) paginator!: MatPaginator;
|
||||
@ViewChild(MatSort) sort!: MatSort;
|
||||
|
||||
displayedColumns: string[] = ['firstName', 'lastName', 'title', 'phone', 'mobile', 'email', 'actions'];
|
||||
dataSource = new MatTableDataSource<any>();
|
||||
contactForm: FormGroup;
|
||||
|
||||
isEditing = false;
|
||||
currentContactId: number | null = null;
|
||||
isLoading = false;
|
||||
showForm = false;
|
||||
showInactiveContacts = false;
|
||||
contacts: Contact[] = [];
|
||||
|
||||
contactReadOnlyFields: any = {
|
||||
lastChangedDate: null,
|
||||
lastChangedBy: null,
|
||||
isInactive: null,
|
||||
inactivatedDate: null
|
||||
};
|
||||
|
||||
private fb = inject(FormBuilder);
|
||||
private dialog = inject(MatDialog);
|
||||
private contactService = inject(ContactService);
|
||||
private notificationService = inject(NotificationService);
|
||||
private errorHandler = inject(ApiErrorHandlerService);
|
||||
|
||||
constructor() {
|
||||
this.contactForm = this.fb.group({
|
||||
firstName: ['', [Validators.required, Validators.maxLength(50)]],
|
||||
lastName: ['', [Validators.required, Validators.maxLength(50)]],
|
||||
middleInitial: ['', [Validators.maxLength(1)]],
|
||||
title: ['', [Validators.required, Validators.maxLength(100)]],
|
||||
phone: ['', [Validators.required, Validators.pattern(/^[0-9]{10,15}$/)]],
|
||||
mobile: ['', [Validators.required, Validators.pattern(/^[0-9]{10,15}$/)]],
|
||||
fax: ['', [Validators.pattern(/^[0-9]{10,15}$/)]],
|
||||
email: ['', [Validators.required, Validators.email, Validators.maxLength(100)]],
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.holderid > 0) {
|
||||
this.loadContacts();
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.dataSource.paginator = this.paginator;
|
||||
this.dataSource.sort = this.sort;
|
||||
}
|
||||
|
||||
loadContacts(): void {
|
||||
this.isLoading = true;
|
||||
|
||||
this.contactService.getContactsById(this.holderid).subscribe({
|
||||
next: (contacts: Contact[]) => {
|
||||
this.contacts = contacts;
|
||||
this.renderContacts();
|
||||
this.isLoading = false;
|
||||
},
|
||||
error: (error: any) => {
|
||||
let errorMessage = this.errorHandler.handleApiError(error, 'Failed to load contacts');
|
||||
this.notificationService.showError(errorMessage);
|
||||
this.isLoading = false;
|
||||
console.error('Error loading contacts:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addNewContact(): void {
|
||||
this.showForm = true;
|
||||
this.isEditing = false;
|
||||
this.currentContactId = null;
|
||||
this.contactForm.reset();
|
||||
}
|
||||
|
||||
editContact(contact: Contact): void {
|
||||
this.showForm = true;
|
||||
this.isEditing = true;
|
||||
this.currentContactId = contact.holdercontactid;
|
||||
|
||||
this.contactForm.patchValue({
|
||||
firstName: contact.firstName,
|
||||
lastName: contact.lastName,
|
||||
middleInitial: contact.middleInitial,
|
||||
title: contact.title,
|
||||
phone: contact.phone,
|
||||
mobile: contact.mobile,
|
||||
fax: contact.fax,
|
||||
email: contact.email
|
||||
});
|
||||
|
||||
this.contactReadOnlyFields.lastChangedDate = contact.lastUpdatedDate ?? contact.dateCreated;
|
||||
this.contactReadOnlyFields.lastChangedBy = contact.lastUpdatedBy ?? contact.createdBy;
|
||||
this.contactReadOnlyFields.isInactive = contact.isInactive;
|
||||
this.contactReadOnlyFields.inactivatedDate = contact.inactivatedDate;
|
||||
}
|
||||
|
||||
saveContact(): void {
|
||||
if (this.contactForm.invalid && !(this.holderid > 0)) {
|
||||
this.contactForm.markAllAsTouched();
|
||||
return;
|
||||
}
|
||||
|
||||
const contactData: Contact = this.contactForm.value;
|
||||
|
||||
const saveObservable = this.isEditing && (this.currentContactId! > 0)
|
||||
? this.contactService.updateContact(this.currentContactId!, contactData)
|
||||
: this.contactService.createContact(this.holderid, contactData);
|
||||
|
||||
saveObservable.subscribe({
|
||||
next: () => {
|
||||
this.notificationService.showSuccess(`Contact ${this.isEditing ? 'updated' : 'added'} successfully`);
|
||||
this.loadContacts();
|
||||
this.cancelEdit();
|
||||
this.hasContacts.emit(true);
|
||||
},
|
||||
error: (error) => {
|
||||
let errorMessage = this.errorHandler.handleApiError(error, `Failed to ${this.isEditing ? 'update' : 'add'} contact`);
|
||||
this.notificationService.showError(errorMessage);
|
||||
console.error('Error saving contact:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
toggleShowInactiveContacts(): void {
|
||||
this.showInactiveContacts = !this.showInactiveContacts;
|
||||
this.renderContacts();
|
||||
}
|
||||
|
||||
renderContacts(): void {
|
||||
if (this.showInactiveContacts) {
|
||||
this.dataSource.data = this.contacts;
|
||||
} else {
|
||||
this.dataSource.data = this.contacts.filter(contact => !contact.isInactive);
|
||||
}
|
||||
}
|
||||
|
||||
cancelEdit(): void {
|
||||
this.showForm = false;
|
||||
this.isEditing = false;
|
||||
this.currentContactId = null;
|
||||
this.contactForm.reset();
|
||||
}
|
||||
|
||||
inactivateContact(contactId: number): void {
|
||||
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
|
||||
width: '350px',
|
||||
data: {
|
||||
title: 'Confirm Inactivation',
|
||||
message: 'Are you sure you want to inactivate this contact?',
|
||||
confirmText: 'Yes',
|
||||
cancelText: 'Cancel'
|
||||
}
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.contactService.inactivateHolderContact(contactId).subscribe({
|
||||
next: () => {
|
||||
this.notificationService.showSuccess('Contact inactivated successfully');
|
||||
this.loadContacts();
|
||||
},
|
||||
error: (error) => {
|
||||
let errorMessage = this.errorHandler.handleApiError(error, 'Failed to inactivate contact');
|
||||
this.notificationService.showError(errorMessage);
|
||||
console.error('Error inactivating contact:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
reactivateContact(contactId: number): void {
|
||||
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
|
||||
width: '350px',
|
||||
data: {
|
||||
title: 'Confirm Reactivation',
|
||||
message: 'Are you sure you want to reactivate this contact?',
|
||||
confirmText: 'Yes',
|
||||
cancelText: 'Cancel'
|
||||
}
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.contactService.reactivateHolderContact(contactId).subscribe({
|
||||
next: () => {
|
||||
this.notificationService.showSuccess('Contact reactivated successfully');
|
||||
this.loadContacts();
|
||||
},
|
||||
error: (error) => {
|
||||
let errorMessage = this.errorHandler.handleApiError(error, 'Failed to reactivate contact');
|
||||
this.notificationService.showError(errorMessage);
|
||||
console.error('Error reactivating contact:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
24
src/app/holder/edit/edit-holder.component.html
Normal file
24
src/app/holder/edit/edit-holder.component.html
Normal file
@ -0,0 +1,24 @@
|
||||
<h2 *ngIf="this.holderName" class="page-header">Manage {{this.holderName}}</h2>
|
||||
|
||||
<div class="holder-action-buttons">
|
||||
<button mat-button (click)="accordion().openAll()">Expand All</button>
|
||||
<button mat-button (click)="accordion().closeAll()">Collapse All</button>
|
||||
</div>
|
||||
<mat-accordion class="holder-headers-align" multi>
|
||||
<mat-expansion-panel>
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title> Basic Details </mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<app-basic-detail [holderid]="holderid" [isEditMode]="isEditMode"
|
||||
(holderName)="onHolderNameUpdate($event)"></app-basic-detail>
|
||||
</mat-expansion-panel>
|
||||
|
||||
<mat-expansion-panel>
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title> Contacts </mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
|
||||
<app-contacts [userPreferences]="userPreferences" [holderid]="holderid"
|
||||
[isEditMode]="isEditMode"></app-contacts>
|
||||
</mat-expansion-panel>
|
||||
</mat-accordion>
|
||||
18
src/app/holder/edit/edit-holder.component.scss
Normal file
18
src/app/holder/edit/edit-holder.component.scss
Normal file
@ -0,0 +1,18 @@
|
||||
.page-header {
|
||||
margin: 0.5rem 0px;
|
||||
color: var(--mat-sys-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.holder-action-buttons {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.holder-headers-align .mat-expansion-panel-header-description {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.holder-headers-align .mat-mdc-form-field+.mat-mdc-form-field {
|
||||
margin-left: 8px;
|
||||
}
|
||||
41
src/app/holder/edit/edit-holder.component.ts
Normal file
41
src/app/holder/edit/edit-holder.component.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { afterNextRender, Component, inject, viewChild } from '@angular/core';
|
||||
import { AngularMaterialModule } from '../../shared/module/angular-material.module';
|
||||
import { MatAccordion } from '@angular/material/expansion';
|
||||
import { UserPreferences } from '../../core/models/user-preference';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { UserPreferencesService } from '../../core/services/user-preference.service';
|
||||
import { BasicDetailComponent } from '../basic-details/basic-details.component';
|
||||
import { ContactsComponent } from '../contacts/contacts.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-edit-holder',
|
||||
imports: [AngularMaterialModule, CommonModule, BasicDetailComponent, ContactsComponent],
|
||||
templateUrl: './edit-holder.component.html',
|
||||
styleUrl: './edit-holder.component.scss'
|
||||
})
|
||||
export class EditHolderComponent {
|
||||
accordion = viewChild.required(MatAccordion);
|
||||
isEditMode = true;
|
||||
holderid = 0;
|
||||
holderName: string | null = null;
|
||||
userPreferences: UserPreferences;
|
||||
|
||||
private route = inject(ActivatedRoute);
|
||||
|
||||
constructor(userPrefenceService: UserPreferencesService) {
|
||||
this.userPreferences = userPrefenceService.getPreferences();
|
||||
afterNextRender(() => {
|
||||
this.accordion().openAll();
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
const idParam = this.route.snapshot.paramMap.get('holderid');
|
||||
this.holderid = idParam ? parseInt(idParam, 10) : 0;
|
||||
}
|
||||
|
||||
onHolderNameUpdate(event: string): void {
|
||||
this.holderName = event;
|
||||
}
|
||||
}
|
||||
122
src/app/holder/search/search-holder.component.html
Normal file
122
src/app/holder/search/search-holder.component.html
Normal file
@ -0,0 +1,122 @@
|
||||
<div class="manage-holder-container">
|
||||
<h3 class="page-header">Search Holders</h3>
|
||||
<div class="search-section">
|
||||
<form class="search-form" [formGroup]="searchForm" (ngSubmit)="onSearch()">
|
||||
<div class="search-fields">
|
||||
<div class="form-row">
|
||||
<mat-form-field appearance="outline" class="name">
|
||||
<mat-label>Holder Name</mat-label>
|
||||
<input matInput formControlName="holderName" placeholder="Enter holder name">
|
||||
<mat-icon matSuffix>search</mat-icon>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="search-actions">
|
||||
<button mat-raised-button color="primary" type="submit">
|
||||
<mat-icon>search</mat-icon>
|
||||
Search
|
||||
</button>
|
||||
<button mat-raised-button type="button" (click)="onClearSearch()">
|
||||
<mat-icon>clear</mat-icon>
|
||||
Clear Search
|
||||
</button>
|
||||
|
||||
<button mat-raised-button color="primary" (click)="addNewHolder()">
|
||||
<mat-icon>add</mat-icon>
|
||||
Add New Holder
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="results-section">
|
||||
<div class="loading-shade" *ngIf="isLoading">
|
||||
<mat-spinner diameter="50"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<div class="actions-bar">
|
||||
<mat-slide-toggle (change)="toggleShowInactiveHolders()" [disabled]="holders.length === 0">
|
||||
Show Inactive Holders
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
|
||||
<table mat-table [dataSource]="dataSource" class="results-table" matSort>
|
||||
<ng-container matColumnDef="holderName">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>Holder Name</th>
|
||||
<td mat-cell *matCellDef="let holder">{{ holder.holderName }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- DBA Name Column -->
|
||||
<ng-container matColumnDef="dbaName">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>DBA Name</th>
|
||||
<td mat-cell *matCellDef="let holder">{{ holder.dbaName || '--'}}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Address Column -->
|
||||
<ng-container matColumnDef="address">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>Address</th>
|
||||
<td mat-cell *matCellDef="let holder">{{ getAddressLabel(holder) }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- USCIB Member Column -->
|
||||
<ng-container matColumnDef="uscibMember">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>USCIB Member</th>
|
||||
<td mat-cell *matCellDef="let holder">{{ holder.uscibMember ? 'Yes' : 'No' }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Holder Column -->
|
||||
<ng-container matColumnDef="holderType">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>Holder Type</th>
|
||||
<td mat-cell *matCellDef="let holder">{{ holder.holderType }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Actions Column -->
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef class="actions-header">Actions</th>
|
||||
<td mat-cell *matCellDef="let holder" class="actions-cell">
|
||||
|
||||
<div class="actions-icons">
|
||||
<button mat-icon-button color="primary" (click)="onEdit(holder.holderid)" matTooltip="Edit">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
|
||||
<button mat-icon-button color="warn" *ngIf="!holder.isInactive" matTooltip="Inactivate"
|
||||
(click)="inactivateHolder(holder.holderid)">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button color="warn" *ngIf="holder.isInactive" matTooltip="Reactivate"
|
||||
(click)="reactivateHolder(holder.holderid)">
|
||||
<mat-icon>loupe</mat-icon>
|
||||
</button>
|
||||
|
||||
<mat-radio-button matTooltip="Select Holder" class="selectHolder" [value]="holder.holderid"
|
||||
[checked]="selectedHolderId === holder.holderid" (change)="onHolderSelection(holder)"
|
||||
aria-label="Select row" [hidden]="holder.isInactive">
|
||||
</mat-radio-button>
|
||||
</div>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
|
||||
<tr matNoDataRow *matNoDataRow>
|
||||
<td [attr.colspan]="displayedColumns.length" class="no-data-message">
|
||||
<mat-icon>info</mat-icon>
|
||||
<span>No records found matching your criteria</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator [length]="dataSource.data.length" [pageSizeOptions]="[userPreferences.pageSize || 2]"
|
||||
[hidePageSize]="true" showFirstLastButtons></mat-paginator>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button mat-raised-button color="primary" type="submit" (click)="saveHolderSelection()">
|
||||
{{ 'Save' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
105
src/app/holder/search/search-holder.component.scss
Normal file
105
src/app/holder/search/search-holder.component.scss
Normal file
@ -0,0 +1,105 @@
|
||||
.page-header {
|
||||
margin: 0.5rem 0px;
|
||||
color: var(--mat-sys-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 16px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.manage-holder-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
width: 100%;
|
||||
|
||||
.search-fields {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
|
||||
.form-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 16px;
|
||||
align-items: start;
|
||||
|
||||
.name {
|
||||
grid-column: span 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 0.5rem;
|
||||
|
||||
button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.results-section {
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
border-radius: 8px;
|
||||
|
||||
.loading-shade {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.actions-icons {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.no-data-message {
|
||||
text-align: center;
|
||||
padding: 0.9rem;
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
|
||||
mat-icon {
|
||||
font-size: 1rem;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
margin-bottom: -3px;
|
||||
}
|
||||
}
|
||||
|
||||
mat-paginator {
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.12);
|
||||
border-radius: 0 0 8px 8px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.manage-holder-container {
|
||||
.search-form {
|
||||
.search-fields {
|
||||
.form-row {
|
||||
grid-template-columns: 1fr;
|
||||
|
||||
.name {
|
||||
grid-column: span 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
218
src/app/holder/search/search-holder.component.ts
Normal file
218
src/app/holder/search/search-holder.component.ts
Normal file
@ -0,0 +1,218 @@
|
||||
import { Component, EventEmitter, inject, Input, Output, ViewChild } from '@angular/core';
|
||||
import { AngularMaterialModule } from '../../shared/module/angular-material.module';
|
||||
import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NavigationService } from '../../core/services/common/navigation.service';
|
||||
import { UserPreferencesService } from '../../core/services/user-preference.service';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { NotificationService } from '../../core/services/common/notification.service';
|
||||
import { ApiErrorHandlerService } from '../../core/services/common/api-error-handler.service';
|
||||
import { UserPreferences } from '../../core/models/user-preference';
|
||||
import { MatSort } from '@angular/material/sort';
|
||||
import { MatPaginator, MatPaginatorIntl } from '@angular/material/paginator';
|
||||
import { HolderService as HolderService } from '../../core/services/holder/holder.service';
|
||||
import { HolderService as CarnetApplicationHolderService } from '../../core/services/carnet/holder.service';
|
||||
import { CustomPaginator } from '../../shared/custom-paginator';
|
||||
import { BasicDetail } from '../../core/models/holder/basic-detail';
|
||||
import { HolderFilter } from '../../core/models/holder/holder-filter';
|
||||
import { ConfirmDialogComponent } from '../../shared/components/confirm-dialog/confirm-dialog.component';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
|
||||
@Component({
|
||||
selector: 'app-holder-search',
|
||||
imports: [AngularMaterialModule, ReactiveFormsModule, CommonModule],
|
||||
templateUrl: './search-holder.component.html',
|
||||
styleUrl: './search-holder.component.scss',
|
||||
providers: [{ provide: MatPaginatorIntl, useClass: CustomPaginator }],
|
||||
})
|
||||
export class SearchHolderComponent {
|
||||
|
||||
@ViewChild(MatPaginator, { static: false })
|
||||
set paginator(value: MatPaginator) {
|
||||
this.dataSource.paginator = value;
|
||||
}
|
||||
|
||||
@ViewChild(MatSort, { static: false })
|
||||
set sort(value: MatSort) {
|
||||
this.dataSource.sort = value;
|
||||
}
|
||||
|
||||
@Input() headerid: number = 0;
|
||||
@Input() selectedHolderId: number = 0;
|
||||
@Input() applicationName: string = '';
|
||||
|
||||
@Output() holderSelectionCompleted = new EventEmitter<boolean>();
|
||||
|
||||
showInactiveHolders: boolean = false;
|
||||
holders: BasicDetail[] = []
|
||||
isLoading: boolean = false;
|
||||
|
||||
userPreferences: UserPreferences;
|
||||
searchForm: FormGroup;
|
||||
|
||||
private fb = inject(FormBuilder);
|
||||
private holderService = inject(HolderService);
|
||||
private navigationService = inject(NavigationService);
|
||||
private carnetHolderService = inject(CarnetApplicationHolderService);
|
||||
private errorHandler = inject(ApiErrorHandlerService);
|
||||
private notificationService = inject(NotificationService);
|
||||
private dialog = inject(MatDialog);
|
||||
|
||||
dataSource = new MatTableDataSource<any>([]);
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.dataSource.paginator = this.paginator;
|
||||
this.dataSource.sort = this.sort;
|
||||
}
|
||||
|
||||
displayedColumns: string[] = ['holderName', 'dbaName', 'address', 'uscibMember', 'holderType', 'actions'];
|
||||
|
||||
constructor(
|
||||
userPrefenceService: UserPreferencesService
|
||||
) {
|
||||
this.userPreferences = userPrefenceService.getPreferences();
|
||||
this.searchForm = this.createSearchForm();
|
||||
}
|
||||
|
||||
createSearchForm(): FormGroup {
|
||||
return this.fb.group({
|
||||
holderName: ['']
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.searchHolders();
|
||||
}
|
||||
|
||||
onSearch(): void {
|
||||
this.searchHolders();
|
||||
}
|
||||
|
||||
saveHolderSelection(): void {
|
||||
const saveObservable = this.carnetHolderService.saveApplicationHolder(this.headerid, this.selectedHolderId ? Number(this.selectedHolderId) : 0);
|
||||
|
||||
saveObservable.subscribe({
|
||||
next: (basicData: any) => {
|
||||
this.notificationService.showSuccess(`Holder updated successfully`);
|
||||
this.holderSelectionCompleted.emit(true);
|
||||
},
|
||||
error: (error: any) => {
|
||||
let errorMessage = this.errorHandler.handleApiError(error, `Failed to update holder details`);
|
||||
this.notificationService.showError(errorMessage);
|
||||
console.error('Error saving holder details:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
toggleShowInactiveHolders(): void {
|
||||
this.showInactiveHolders = !this.showInactiveHolders;
|
||||
this.renderHolders();
|
||||
}
|
||||
|
||||
searchHolders(): void {
|
||||
this.isLoading = true;
|
||||
const filterData: HolderFilter = this.searchForm.value;
|
||||
|
||||
this.holderService.getHolders(filterData).subscribe({
|
||||
next: (holders: BasicDetail[]) => {
|
||||
this.dataSource.data = this.holders = holders;
|
||||
this.renderHolders()
|
||||
this.isLoading = false;
|
||||
},
|
||||
error: (error: any) => {
|
||||
let errorMessage = this.errorHandler.handleApiError(error, 'Failed to search holders');
|
||||
this.notificationService.showError(errorMessage);
|
||||
this.isLoading = false;
|
||||
console.error('Error loading holders:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
renderHolders() {
|
||||
if (this.showInactiveHolders) {
|
||||
this.dataSource.data = this.holders;
|
||||
} else {
|
||||
this.dataSource.data = this.holders.filter((holder: any) => !holder?.isInactive);
|
||||
}
|
||||
}
|
||||
|
||||
addNewHolder(): void {
|
||||
this.navigationService.navigate(["add-holder"], { state: { isEditMode: false } })
|
||||
}
|
||||
|
||||
onEdit(id: string) {
|
||||
this.navigationService.navigate(['edit-holder', id]);
|
||||
}
|
||||
|
||||
inactivateHolder(holderid: number): void {
|
||||
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
|
||||
width: '350px',
|
||||
data: {
|
||||
title: 'Confirm Inactivation',
|
||||
message: 'Are you sure you want to inactivate this holder?',
|
||||
confirmText: 'Yes',
|
||||
cancelText: 'Cancel'
|
||||
}
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.holderService.inactivateHolder(holderid).subscribe({
|
||||
next: () => {
|
||||
this.notificationService.showSuccess('Holder inactivated successfully');
|
||||
this.searchHolders();
|
||||
},
|
||||
error: (error) => {
|
||||
let errorMessage = this.errorHandler.handleApiError(error, 'Failed to inactivate holder');
|
||||
this.notificationService.showError(errorMessage);
|
||||
console.error('Error inactivating holder:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
reactivateHolder(holderid: number): void {
|
||||
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
|
||||
width: '350px',
|
||||
data: {
|
||||
title: 'Confirm Reactivation',
|
||||
message: 'Are you sure you want to reactivate this holder?',
|
||||
confirmText: 'Yes',
|
||||
cancelText: 'Cancel'
|
||||
}
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.holderService.reactivateHolder(holderid).subscribe({
|
||||
next: () => {
|
||||
this.notificationService.showSuccess('Holder reactivated successfully');
|
||||
this.searchHolders();
|
||||
},
|
||||
error: (error) => {
|
||||
let errorMessage = this.errorHandler.handleApiError(error, 'Failed to reactivate holder');
|
||||
this.notificationService.showError(errorMessage);
|
||||
console.error('Error reactivating holder:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onClearSearch(): void {
|
||||
this.searchForm.reset();
|
||||
this.searchHolders();
|
||||
}
|
||||
|
||||
onHolderSelection(holder: any): void {
|
||||
this.selectedHolderId = holder.holderid;
|
||||
}
|
||||
|
||||
getAddressLabel(holder: BasicDetail): string {
|
||||
|
||||
return `${holder.address1}, ${holder.address2}
|
||||
${holder.city}, ${holder.state}, ${holder.zip},
|
||||
${holder.country}`;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user