diff --git a/src/app/core/models/param/country-message.ts b/src/app/core/models/param/country-message.ts new file mode 100644 index 0000000..9ab703f --- /dev/null +++ b/src/app/core/models/param/country-message.ts @@ -0,0 +1,6 @@ +import { ParamProperties } from "./parameters"; + +export interface CountryMessage extends ParamProperties { + countryMessage: string | null; + isInactive: boolean; +} diff --git a/src/app/core/models/param/parameters.ts b/src/app/core/models/param/parameters.ts index f930ab6..43f174d 100644 --- a/src/app/core/models/param/parameters.ts +++ b/src/app/core/models/param/parameters.ts @@ -5,18 +5,18 @@ export interface ParamProperties { paramType: string; paramDesc: string; paramValue: string; - addlParamValue1: string | null; - addlParamValue2: string | null; - addlParamValue3: string | null; - addlParamValue4: string | null; - addlParamValue5: string | null; + addlParamValue1?: string | null; + addlParamValue2?: string | null; + addlParamValue3?: string | null; + addlParamValue4?: string | null; + addlParamValue5?: string | null; sortSeq?: number; - inactiveCodeFlag: 'Y' | 'N' | null; - inactiveDate: string | null; - createdBy: string; - dateCreated: string; - lastUpdatedBy: string | null; - lastUpdatedDate: string | null; - errorMesg: string | null; + inactiveCodeFlag?: 'Y' | 'N' | null; + inactiveDate?: string | null; + createdBy?: string; + dateCreated?: string; + lastUpdatedBy?: string | null; + lastUpdatedDate?: string | null; + errorMesg?: string | null; userId?: string; } diff --git a/src/app/core/models/service-provider/contact-login.ts b/src/app/core/models/service-provider/contact-login.ts new file mode 100644 index 0000000..a7f3200 --- /dev/null +++ b/src/app/core/models/service-provider/contact-login.ts @@ -0,0 +1,4 @@ +export interface ContactLogin { + spid: number; + emailAddress: string; +} diff --git a/src/app/core/models/service-provider/contact.ts b/src/app/core/models/service-provider/contact.ts index 0585a2c..06860ec 100644 --- a/src/app/core/models/service-provider/contact.ts +++ b/src/app/core/models/service-provider/contact.ts @@ -16,4 +16,5 @@ export interface Contact { lastUpdatedDate?: Date | null; isInactive?: boolean | null; inactivatedDate?: Date | null; + hasLogin?: boolean | null; } diff --git a/src/app/core/services/common/api-error-handler.service.ts b/src/app/core/services/common/api-error-handler.service.ts index 9cf5301..1e76cd5 100644 --- a/src/app/core/services/common/api-error-handler.service.ts +++ b/src/app/core/services/common/api-error-handler.service.ts @@ -36,7 +36,12 @@ export class ApiErrorHandlerService { return this.genericErrorMessage; } - return messageString.slice(0, -1); + // slice off the last pipe character only if the last character is a pipe + if (messageString.endsWith('|')) { + return messageString.slice(0, -1); + } + + return messageString; // .split('|') // .map(msg => msg.trim()) // .filter(msg => msg.length > 0); diff --git a/src/app/core/services/param/param.service.ts b/src/app/core/services/param/param.service.ts index 8538164..053d703 100644 --- a/src/app/core/services/param/param.service.ts +++ b/src/app/core/services/param/param.service.ts @@ -42,7 +42,6 @@ export class ParamService { lastUpdatedDate: paramValue.LASTUPDATEDDATE, errorMesg: paramValue.ERRORMESG, })); - } createTableRecord(description: string): Observable { diff --git a/src/app/core/services/service-provider/contact.service.ts b/src/app/core/services/service-provider/contact.service.ts index 06ed196..a410bd0 100644 --- a/src/app/core/services/service-provider/contact.service.ts +++ b/src/app/core/services/service-provider/contact.service.ts @@ -4,6 +4,7 @@ import { map, Observable } from 'rxjs'; import { environment } from '../../../../environments/environment'; import { Contact } from '../../models/service-provider/contact'; import { UserService } from '../common/user.service'; +import { ContactLogin } from '../../models/service-provider/contact-login'; @Injectable({ providedIn: 'root' @@ -38,7 +39,8 @@ export class ContactService { lastUpdatedBy: contact.LASTUPDATEDBY || null, lastUpdatedDate: contact.LASTUPDATEDDATE || null, isInactive: contact.INACTIVEFLAG === 'Y' || false, - inactivatedDate: contact.INACTIVEDATE || null + inactivatedDate: contact.INACTIVEDATE || null, + hasLogin: contact.LOGINFLAG === 'Y' || false })); } @@ -82,4 +84,18 @@ export class ContactService { deleteContact(spContactId: string): Observable { return this.http.patch(`${this.apiUrl}/${this.apiDb}/InactivateSPContact/${spContactId}`, null); } + + createContactLogin(data: ContactLogin): Observable { + const contact = { + P_SPID: data.spid, + P_EMAILADDR: data.emailAddress, + P_ENABLEPASSWORDPOLICY: 'Y', + P_DOMAIN: 'test', + P_LOOKUPCODE: 'test', + P_USERID: this.userService.getUser() + } + + return this.http.post(`${this.apiUrl}/${this.apiDb}/CreateSPLogins`, contact); + } + } diff --git a/src/app/param/manage-country/manage-country.component.html b/src/app/param/manage-country/manage-country.component.html new file mode 100644 index 0000000..a8d51e5 --- /dev/null +++ b/src/app/param/manage-country/manage-country.component.html @@ -0,0 +1,81 @@ + +
+ + +
+
+
+ + Country + + + {{ country.paramDesc }} + + + +
+ +
+ + Message + + + Message is required + + +
+ +
+ + Type of Action + + + {{ actionType }} + + + + + + Action + + +
+ + + +
+ + + +
+
+
+ +
\ No newline at end of file diff --git a/src/app/param/manage-country/manage-country.component.scss b/src/app/param/manage-country/manage-country.component.scss new file mode 100644 index 0000000..d22a3aa --- /dev/null +++ b/src/app/param/manage-country/manage-country.component.scss @@ -0,0 +1,105 @@ +.page-header { + margin: 0.5rem 0px; + color: var(--mat-sys-primary); + font-weight: 500; +} + +.manage-container { + display: flex; + flex-direction: column; + gap: 0.5rem; + width: 100%; +} + +.form-container { + margin-top: 0.5rem; + + .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; + align-items: start; + + mat-form-field { + flex: 1; + } + + .small-field { + max-width: 120px; + } + } + + .form-actions { + display: flex; + justify-content: flex-end; + gap: 16px; + margin-top: 16px; + } + + .readonly-section { + padding-top: 0.5rem; + border-top: 1px solid #eee; + + .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.875rem; + 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; + } +} \ No newline at end of file diff --git a/src/app/param/manage-country/manage-country.component.ts b/src/app/param/manage-country/manage-country.component.ts new file mode 100644 index 0000000..245ea08 --- /dev/null +++ b/src/app/param/manage-country/manage-country.component.ts @@ -0,0 +1,258 @@ +import { CommonModule } from '@angular/common'; +import { Component, inject } from '@angular/core'; +import { ReactiveFormsModule, FormGroup, Validators, FormBuilder } from '@angular/forms'; +import { finalize, forkJoin } from 'rxjs'; +import { ParamProperties } from '../../core/models/param/parameters'; +import { ApiErrorHandlerService } from '../../core/services/common/api-error-handler.service'; +import { NotificationService } from '../../core/services/common/notification.service'; +import { ParamService } from '../../core/services/param/param.service'; +import { AngularMaterialModule } from '../../shared/module/angular-material.module'; +import { MatSelectChange } from '@angular/material/select'; +import { CountryMessage } from '../../core/models/param/country-message'; +import { MatDialog } from '@angular/material/dialog'; +import { ConfirmDialogComponent } from '../../shared/components/confirm-dialog/confirm-dialog.component'; + +@Component({ + selector: 'app-manage-country', + imports: [AngularMaterialModule, CommonModule, ReactiveFormsModule], + templateUrl: './manage-country.component.html', + styleUrl: './manage-country.component.scss' +}) +export class ManageCountryComponent { + isLoading: boolean = false; + changeInProgress: boolean = false; + countries: CountryMessage[] = []; + countriesWithMessages: ParamProperties[] = []; + currentCountryId: number = 0; + currentCountry: CountryMessage | undefined | null = null; + actionTypes: string[] = ['WARN', 'FYI', 'ACTION']; + + private paramService = inject(ParamService); + private errorHandler = inject(ApiErrorHandlerService); + private notificationService = inject(NotificationService); + private fb = inject(FormBuilder); + private dialog = inject(MatDialog); + + countryForm: FormGroup; + + constructor() { + this.countryForm = this.initializeForm(); + } + + initializeForm(): FormGroup { + return this.fb.group({ + countryMessage: ['', [Validators.required]], + actionType: [{ value: '' }], + action: [{ value: '', disabled: true }] + }); + } + + ngOnInit(): void { + this.getCountries(); + } + + onCountrySelectionChanged(event: MatSelectChange) { + this.renderCountry(+event.value); + } + + renderCountry(countryid: number): void { + this.currentCountry = this.countries.find(c => c.paramId === countryid); + this.currentCountryId = this.countriesWithMessages.find(c => c.paramValue === this.currentCountry?.paramValue)?.paramId ?? 0; + this.patchCountryData(); + } + + onActionTypeSelectionChanged(event: MatSelectChange) { + if (event.value === 'ACTION') { + this.countryForm.get('action')?.setValue('TRANSIT'); + } + } + + getCountries(): void { + this.isLoading = true; + + forkJoin({ + countries: this.paramService.getParameters('002'), + countriesWithMessages: this.paramService.getParameters('014') + }).pipe(finalize(() => { + this.isLoading = false; + })).subscribe({ + next: (results) => { + this.countries = this.joinCountriesWithMessages(results.countries, results.countriesWithMessages); + this.countriesWithMessages = results.countriesWithMessages; + + if (this.currentCountry) { + this.renderCountry(this.currentCountry.paramId!); + } + }, + error: (error) => { + console.error('Error loading countries messages data', error); + let errorMessage = this.errorHandler.handleApiError(error, 'Failed to load countries messages data'); + this.notificationService.showError(errorMessage); + } + }); + } + + patchCountryData(): void { + if (!this.currentCountry) { + return; + } + + this.countryForm.patchValue({ + countryMessage: this.currentCountry.countryMessage, + actionType: this.currentCountry.addlParamValue1, + action: this.currentCountry.addlParamValue2 + }); + + if (this.currentCountry.addlParamValue1 === 'ACTION') { + this.countryForm.get('action')?.setValue('TRANSIT'); + } else { + this.countryForm.get('action')?.setValue(''); + } + } + + saveRecord(): void { + if (this.countryForm.invalid) { + this.countryForm.markAllAsTouched(); + return; + } + + const paramData: ParamProperties = { + paramDesc: this.countryForm.value.countryMessage, + paramType: '014', + paramValue: this.currentCountry?.paramValue ?? '', + addlParamValue1: this.countryForm.value.actionType, + addlParamValue2: this.countryForm.value.actionType === 'ACTION' ? 'TRANSIT' : null, + sortSeq: 1, + paramId: this.currentCountryId ?? 0 + }; + + const saveObservable = this.currentCountryId > 0 + ? this.paramService.updateParamRecord(paramData) + : this.paramService.createParamRecord(paramData); + + this.changeInProgress = true; + saveObservable.pipe(finalize(() => { + this.changeInProgress = false; + })).subscribe({ + next: () => { + this.notificationService.showSuccess(`Country message updated successfully`); + this.getCountries(); + }, + error: (error) => { + let errorMessage = this.errorHandler.handleApiError(error, `Failed to update country message`); + this.notificationService.showError(errorMessage); + console.error('Error saving country message:', error); + } + }); + } + + inActivateParamRecord(): void { + if (this.currentCountryId === 0) { + this.notificationService.showError('Not a valid country to inactivate'); + return; + } + + const dialogRef = this.dialog.open(ConfirmDialogComponent, { + width: '350px', + data: { + title: 'Inactivate Country Message', + message: 'Are you sure you want to inactivate this message?', + confirmText: 'Yes', + cancelText: 'Cancel' + } + }); + + dialogRef.afterClosed().subscribe(result => { + if (result) { + this.paramService.inActivateParamRecord(this.currentCountryId).subscribe( + { + next: (data: any) => { + this.notificationService.showSuccess(`Country message inactivated successfully`); + this.getCountries(); + }, + error: (error: any) => { + let errorMessage = this.errorHandler.handleApiError(error, `Failed to inactivate country message`); + this.notificationService.showError(errorMessage); + console.error('Error inactivating country message:', error); + } + } + ); + } + }); + } + + reActivateParamRecord(): void { + if (this.currentCountryId === 0) { + this.notificationService.showError('Not a valid country to reactivate'); + return; + } + const dialogRef = this.dialog.open(ConfirmDialogComponent, { + width: '350px', + data: { + title: 'Reactivate Country Message', + message: 'Are you sure you want to reactivate this message?', + confirmText: 'Yes', + cancelText: 'Cancel' + } + }); + + dialogRef.afterClosed().subscribe(result => { + if (result) { + this.paramService.reActivateParamRecord(this.currentCountryId).subscribe( + { + next: (data: any) => { + this.notificationService.showSuccess(`Country message reactivated successfully`) + this.getCountries(); + }, + error: (error: any) => { + let errorMessage = this.errorHandler.handleApiError(error, `Failed to reactivate country message`); + this.notificationService.showError(errorMessage); + console.error('Error reactivating country message:', error); + } + } + ); + } + }); + } + + joinCountriesWithMessages(countries: ParamProperties[], countryMessages: ParamProperties[]): CountryMessage[] { + // Group messages by paramvalue + const messagesByCountry = countryMessages.reduce((acc, message) => { + if (!acc[message.paramValue]) { + acc[message.paramValue] = { + messages: [], + // paramId: message.paramId, // Store one paramid from messages + isInactive: message.inactiveCodeFlag === 'N', + addlParamValue1: message.addlParamValue1, + addlParamValue2: message.addlParamValue2 + }; + } + acc[message.paramValue].messages.push(message.paramDesc); + return acc; + }, {} as Record); + + // Join countries with their messages + return countries.map(country => { + const countryMessages = messagesByCountry[country.paramValue] || { + messages: [], //paramId: null, + isInactive: false, + addlParamValue1: null, + addlParamValue2: null + }; + + return { + ...country, + countryMessage: countryMessages.messages.join(' '), + isInactive: countryMessages.isInactive, + addlParamValue1: countryMessages.addlParamValue1, + addlParamValue2: countryMessages.addlParamValue2, + paramType: '014', + }; + }); + } +} \ No newline at end of file diff --git a/src/app/service-provider/basic-fee/basic-fee.component.ts b/src/app/service-provider/basic-fee/basic-fee.component.ts index 658f116..82e7cfe 100644 --- a/src/app/service-provider/basic-fee/basic-fee.component.ts +++ b/src/app/service-provider/basic-fee/basic-fee.component.ts @@ -98,7 +98,7 @@ export class BasicFeeComponent { renderRecods(): void { if (this.showExpiredRecords) { - this.dataSource.data = this.basicFees; + this.dataSource.data = this.basicFees.filter(record => record.expired); } else { this.dataSource.data = this.basicFees.filter(record => !record.expired); } diff --git a/src/app/service-provider/carnet-fee/carnet-fee.component.scss b/src/app/service-provider/carnet-fee/carnet-fee.component.scss index 1b67170..aca4f59 100644 --- a/src/app/service-provider/carnet-fee/carnet-fee.component.scss +++ b/src/app/service-provider/carnet-fee/carnet-fee.component.scss @@ -1,5 +1,5 @@ .fee-commission-container { - padding: 24px; + padding: 0.5rem; display: flex; flex-direction: column; gap: 24px; @@ -12,7 +12,7 @@ transform: scale(0.8); margin-left: -0.5rem; } - + button { float: right; } diff --git a/src/app/service-provider/carnet-fee/carnet-fee.component.ts b/src/app/service-provider/carnet-fee/carnet-fee.component.ts index d18a023..b970b3d 100644 --- a/src/app/service-provider/carnet-fee/carnet-fee.component.ts +++ b/src/app/service-provider/carnet-fee/carnet-fee.component.ts @@ -100,7 +100,7 @@ export class CarnetFeeComponent implements OnInit { renderRecods(): void { if (this.showExpiredRecords) { - this.dataSource.data = this.carnetFees; + this.dataSource.data = this.carnetFees.filter(record => record.expired); } else { this.dataSource.data = this.carnetFees.filter(record => !record.expired); } diff --git a/src/app/service-provider/contacts/contacts.component.html b/src/app/service-provider/contacts/contacts.component.html index 1efc24d..a60d05b 100644 --- a/src/app/service-provider/contacts/contacts.component.html +++ b/src/app/service-provider/contacts/contacts.component.html @@ -66,6 +66,11 @@ +