manage preparer changes

This commit is contained in:
Cyril Joseph 2025-06-08 00:03:38 -03:00
parent fadfa87cdb
commit 12be0fb8c7
20 changed files with 1332 additions and 93 deletions

View File

@ -6,6 +6,9 @@ import { HomeComponent } from './home/home.component';
import { AppIdGuard } from './guards/appid.guard';
import { EditServiceProviderComponent } from './service-provider/edit/edit-service-provider.component';
import { UserSettingsComponent } from './user-settings/user-settings.component';
import { ManagePreparerComponent } from './preparer/manage/manage-preparer.component';
import { EditPreparerComponent } from './preparer/edit/edit-preparer.component';
import { AddPreparerComponent } from './preparer/add/add-preparer.component';
export const routes: Routes = [
{ path: 'login', component: LoginComponent },
@ -16,6 +19,9 @@ export const routes: Routes = [
{ path: 'home', component: HomeComponent },
{ path: 'usersettings', component: UserSettingsComponent },
{ path: 'service-provider/:id', component: EditServiceProviderComponent },
{ path: 'preparer', component: ManagePreparerComponent },
{ path: 'preparer/:id', component: EditPreparerComponent },
{ path: 'add-preparer', component: AddPreparerComponent },
{ path: '', redirectTo: 'home', pathMatch: 'full' }
],
canActivate: [AuthGuard, AppIdGuard]

View File

@ -1,10 +1,17 @@
export interface Location {
id: number;
clientId: number;
locationName: string;
locationid: number;
clientid: number;
name: string;
address1: string;
address2?: string | null;
city: string;
state: string;
country: string;
zip: string;
dateCreated?: Date | null;
createdBy?: string | null;
lastUpdatedBy?: string | null;
lastUpdatedDate?: Date | null;
isInactive?: boolean | null; // TODO
inactivatedDate?: Date | null; // TODO
}

View File

@ -39,18 +39,18 @@ export class BasicDetailService {
createBasicDetails(data: BasicDetail): Observable<any> {
const basicDetails = {
p_spid: this.userService.getUserSpid(),
p_clientname: data.name,
p_lookupcode: data.lookupCode,
p_address1: data.address1,
p_address2: data.address2,
p_city: data.city,
p_state: data.state,
p_country: data.country,
p_zip: data.zip,
p_issuingregion: data.carnetIssuingRegion,
p_revenuelocation: data.revenueLocation,
p_userid: this.userService.getUser(),
P_SPID: this.userService.getUserSpid(),
P_CLIENTNAME: data.name,
P_LOOKUPCODE: data.lookupCode,
P_ADDRESS1: data.address1,
P_ADDRESS2: data.address2,
P_CITY: data.city,
P_STATE: data.state,
P_COUNTRY: data.country,
P_ZIP: data.zip,
P_ISSUINGREGION: data.carnetIssuingRegion,
P_REVENUELOCATION: data.revenueLocation,
P_USERID: this.userService.getUser(),
}
return this.http.post(`${this.apiUrl}/${this.apiDb}/CreateNewClients`, basicDetails);
@ -58,18 +58,18 @@ export class BasicDetailService {
updateBasicDetails(id: number, data: BasicDetail): Observable<any> {
const basicDetails = {
p_spid: this.userService.getUserSpid(),
p_clientid: id,
p_clientname: data.name,
p_lookupcode: data.lookupCode,
p_address1: data.address1,
p_address2: data.address2,
p_city: data.city,
p_state: data.state,
p_country: data.country,
p_zip: data.zip,
p_revenuelocation: data.revenueLocation,
p_userid: this.userService.getUser(),
P_SPID: this.userService.getUserSpid(),
P_CLIENTID: id,
P_PREPARERNAME: data.name,
// P_LOOKUPCODE: data.lookupCode,
P_ADDRESS1: data.address1,
P_ADDRESS2: data.address2,
P_CITY: data.city,
P_STATE: data.state,
P_COUNTRY: data.country,
P_ZIP: data.zip,
P_REVENUELOCATION: data.revenueLocation,
P_USERID: this.userService.getUser(),
}
return this.http.put(`${this.apiUrl}/${this.apiDb}/UpdateClient`, basicDetails);

View File

@ -1,9 +1,46 @@
import { Injectable } from '@angular/core';
import { environment } from '../../../../environments/environment';
import { HttpClient } from '@angular/common/http';
import { UserService } from '../common/user.service';
import { BasicDetail } from '../../models/preparer/basic-detail';
import { map, Observable } from 'rxjs';
import { PreparerFilter } from '../../models/preparer/preparer-filter';
@Injectable({
providedIn: 'root'
})
export class ClientService {
private apiUrl = environment.apiUrl;
private apiDb = environment.apiDb;
constructor(private http: HttpClient, private userService: UserService) { }
getPreparers(filter: PreparerFilter): Observable<BasicDetail[]> {
return this.http.get<any[]>(`${this.apiUrl}/${this.apiDb}/GetPreparers?P_SPID=${this.userService.getUserSpid()}&P_STATUS=ACTIVE&P_NAME=${filter.name}&P_LOOKUPCODE=${filter.lookupCode}&P_CITY=${filter.city}&P_STATE=${filter.state}`).pipe(
map(response => this.mapToClients(response)));
}
private mapToClients(data: any[]): BasicDetail[] {
return data.map(basicDetails => ({
clientid: basicDetails.CLIENTID,
spid: basicDetails.SPID,
name: basicDetails.PREPARERNAME,
lookupCode: basicDetails.LOOKUPCODE,
address1: basicDetails.ADDRESS1,
address2: basicDetails.ADDRESS2,
city: basicDetails.CITY,
state: basicDetails.STATE,
country: basicDetails.COUNTRY,
carnetIssuingRegion: basicDetails.ISSUINGREGION,
revenueLocation: basicDetails.REVENUELOCATION,
zip: basicDetails.ZIP,
// createdBy: basicDetails.CREATEDBY || null,
// dateCreated: basicDetails.DATECREATED || null,
// lastUpdatedBy: basicDetails.LASTUPDATEDBY || null,
// lastUpdatedDate: basicDetails.LASTUPDATEDDATE || null,
// isInactive: basicDetails.INACTIVEFLAG === 'Y' || false,
// inactivatedDate: basicDetails.INACTIVEDATE || null
}));
}
constructor() { }
}

View File

@ -44,20 +44,20 @@ export class ContactService {
createContact(clientid: number, data: Contact): Observable<any> {
const contact = {
p_spid: this.userService.getUserSpid(),
p_clientid: clientid,
p_defcontactflag: data.defaultContact ? 'Y' : 'N',
p_contactstable: [{
FirstName: data.firstName,
LastName: data.lastName,
MiddleInitial: data.middleInitial,
Title: data.title,
EmailAddress: data.email,
MobileNo: data.mobile,
PhoneNo: data.phone,
FaxNo: data.fax
P_SPID: this.userService.getUserSpid(),
P_CLIENTID: clientid,
P_DEFCONTACTFLAG: data.defaultContact ? 'Y' : 'N',
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_user_id: this.userService.getUser()
P_USERID: this.userService.getUser()
}
return this.http.post(`${this.apiUrl}/${this.apiDb}/CreateClientContacts`, contact);
@ -65,17 +65,17 @@ export class ContactService {
updateContact(spContactId: number, data: Contact): Observable<any> {
const contact = {
p_spid: this.userService.getUserSpid(),
p_clientcontactid: spContactId,
p_firstname: data.firstName,
p_lastname: data.lastName,
P_middleinitial: data.middleInitial,
p_title: data.title,
p_phone: data.phone,
p_mobileno: data.mobile,
p_fax: data.fax,
p_emailaddress: data.email,
p_user_id: this.userService.getUser()
P_SPID: this.userService.getUserSpid(),
P_CLIENTCONTACTID: spContactId,
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}/UpdateClientContacts`, contact);

View File

@ -1,9 +1,82 @@
import { 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 { Location } from '../../models/preparer/location';
@Injectable({
providedIn: 'root'
})
export class LocationService {
private apiUrl = environment.apiUrl;
private apiDb = environment.apiDb;
constructor() { }
constructor(private http: HttpClient, private userService: UserService) { }
getLocationsById(id: number): Observable<Location[]> {
return this.http.get<any[]>(`${this.apiUrl}/${this.apiDb}/GetPreparerLocByClientid?p_spid=${this.userService.getUserSpid()}&p_clientid=${id}`).pipe(
map(response => this.mapToLocations(response)));
}
private mapToLocations(data: any[]): Location[] {
return data.map(location => ({
locationid: location.LOCATIONID,
spid: location.SPID,
clientid: location.CLIENTID,
name: location.NAMEOF,
address1: location.ADDRESS1,
address2: location.ADDRESS2,
city: location.CITY,
state: location.STATE,
country: location.COUNTRY,
zip: location.ZIP,
createdBy: location.CREATEDBY || null,
dateCreated: location.DATECREATED || null,
lastUpdatedBy: location.LASTUPDATEDBY || null,
lastUpdatedDate: location.LASTUPDATEDDATE || null,
isInactive: location.INACTIVEFLAG === 'Y' || false,
inactivatedDate: location.INACTIVEDATE || null
}));
}
createLocation(clientid: number, data: Location): Observable<any> {
const location = {
P_SPID: this.userService.getUserSpid(),
P_CLIENTID: clientid,
P_CLIENTLOCADDRESSTABLE: [{
P_NAMEOF: data.name,
P_ADDRESS1: data.address1,
P_ADDRESS2: data.address2,
P_CITY: data.city,
P_STATE: data.state,
P_COUNTRY: data.country,
P_ZIP: data.zip,
}],
P_USERID: this.userService.getUser()
}
return this.http.post(`${this.apiUrl}/${this.apiDb}/CreateClientLocations`, location);
}
updateLocation(locationId: number, data: Location): Observable<any> {
const location = {
P_SPID: this.userService.getUserSpid(),
P_CLIENTLOCATIONID: locationId,
P_LOCATIONNAME: data.name,
P_ADDRESS1: data.address1,
P_ADDRESS2: data.address2,
P_CITY: data.city,
P_STATE: data.state,
P_COUNTRY: data.country,
P_ZIP: data.zip,
P_USERID: this.userService.getUser()
}
return this.http.put(`${this.apiUrl}/${this.apiDb}/UpdateClientLocations`, location);
}
// deleteLocation(clientContactId: string): Observable<any> {
// return this.http.post(`${this.apiUrl}/${this.apiDb}/InactivateSPContact?p_clientcontactid=${clientContactId}`, null);
// }
}

View File

@ -20,13 +20,13 @@
</mat-step>
<!-- Location Step -->
<!-- <mat-step [completed]="locationCompleted" [editable]="!!clientid && contactsCompleted">
<mat-step [completed]="locationCompleted" [editable]="!!clientid && contactsCompleted">
<ng-template matStepLabel>Locations</ng-template>
<app-location *ngIf="clientid" [clientid]="clientid" (hasLocation)="onLocationSaved($event)"
<app-location *ngIf="clientid" [clientid]="clientid" (hasLocations)="onLocationSaved($event)"
[userPreferences]="userPreferences">
</app-location>
</mat-step> -->
</mat-step>
</mat-stepper>
</div>

View File

@ -1,6 +1,6 @@
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { Subject, takeUntil, zip } from 'rxjs';
import { Subject, takeUntil } from 'rxjs';
import { AngularMaterialModule } from '../../shared/module/angular-material.module';
import { CommonModule } from '@angular/common';
import { Country } from '../../core/models/country';
@ -75,10 +75,10 @@ export class BasicDetailsComponent implements OnInit, OnDestroy {
createForm(): FormGroup {
return this.fb.group({
name: ['', [Validators.required, Validators.maxLength(100)]],
lookupCode: ['', Validators.required, Validators.maxLength(20)],
address1: ['', Validators.required, Validators.maxLength(100)],
address2: ['', [Validators.maxLength(100)]],
city: ['', Validators.required, Validators.maxLength(50)],
lookupCode: ['', [Validators.required, Validators.maxLength(20)]],
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')]],
@ -88,7 +88,7 @@ export class BasicDetailsComponent implements OnInit, OnDestroy {
}
loadLookupData(): void {
this.commonService.getCountries(this.clientid)
this.commonService.getCountries(0)
.pipe(takeUntil(this.destroy$))
.subscribe({
next: (countries) => {
@ -126,13 +126,8 @@ export class BasicDetailsComponent implements OnInit, OnDestroy {
.subscribe({
next: (states) => {
this.states = states;
const stateControl = this.basicDetailsForm.get('state');
if (this.countriesHasStates.includes(country)) {
stateControl?.enable();
} else {
stateControl?.disable();
stateControl?.setValue('FN');
}
this.updateStateControl('state', country);
this.updateStateControl('revenueLocation', country);
this.isLoading = false;
},
error: (error) => {
@ -142,6 +137,16 @@ export class BasicDetailsComponent implements OnInit, OnDestroy {
});
}
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({
name: data.name,

View File

@ -1,5 +1,9 @@
<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>
@ -35,6 +39,12 @@
<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>
@ -59,13 +69,17 @@
<mat-icon>edit</mat-icon>
</button>
<button mat-icon-button color="primary" (click)="createLogin()" matTooltip="Login">
<mat-icon>passkey</mat-icon>
<mat-icon>person</mat-icon>
</button>
<button mat-icon-button color="warn" *ngIf="!contact.defaultContact || !contact.isInactive" (click)="
deleteContact(contact.clientContactid)" [hidden]="contact.defaultContact || contact.isInactive"
matTooltip="Inactivate">
<mat-icon>delete</mat-icon>
</button>
<button mat-icon-button color="warn" *ngIf="!contact.isInactive" (click)="
deleteContact(contact.clientContactid)" [hidden]="contact.isInactive" matTooltip="Inactivate">
<mat-icon>delete</mat-icon>
</button>
<!-- <button mat-icon-button (click)="setDefaultContact(contact.contactId)"
[color]="contact.defaultContact ? 'primary' : ''" matTooltip="Set as default">
<mat-icon>star</mat-icon>

View File

@ -8,6 +8,11 @@
clear: both;
margin-bottom: -16px;
mat-slide-toggle {
transform: scale(0.8);
margin-left: -0.5rem;
}
button {
float: right;
}

View File

@ -26,14 +26,15 @@ export class ContactsComponent {
@ViewChild(MatPaginator) paginator!: MatPaginator;
@ViewChild(MatSort) sort!: MatSort;
displayedColumns: string[] = ['firstName', 'lastName', 'title', 'phone', 'email', 'defaultContact', 'actions'];
displayedColumns: string[] = ['firstName', 'lastName', 'title', 'phone', 'mobile', 'email', 'defaultContact', '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,
@ -79,7 +80,8 @@ export class ContactsComponent {
this.contactService.getContactsById(this.clientid).subscribe({
next: (contacts: Contact[]) => {
this.dataSource.data = contacts;
this.contacts = contacts;
this.renderContacts();
this.isLoading = false;
},
error: (error: any) => {
@ -91,15 +93,6 @@ export class ContactsComponent {
});
}
// applyFilter(event: Event): void {
// const filterValue = (event.target as HTMLInputElement).value;
// this.dataSource.filter = filterValue.trim().toLowerCase();
// if (this.dataSource.paginator) {
// this.dataSource.paginator.firstPage();
// }
// }
addNewContact(): void {
this.showForm = true;
this.isEditing = false;
@ -159,6 +152,19 @@ export class ContactsComponent {
});
}
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);
}
}
deleteContact(contactId: string): void {
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
width: '350px',

View File

@ -19,12 +19,11 @@
</mat-expansion-panel-header>
<app-contacts [clientid]="clientid" [userPreferences]="userPreferences"></app-contacts>
</mat-expansion-panel>
<!--
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title> Locations </mat-panel-title>
</mat-expansion-panel-header>
<app-location [clientid]="clientid" [isEditMode]="isEditMode"
[userPreferences]="userPreferences"></app-location>
</mat-expansion-panel> -->
<app-location [clientid]="clientid" [userPreferences]="userPreferences"></app-location>
</mat-expansion-panel>
</mat-accordion>

View File

@ -7,10 +7,11 @@ import { UserPreferences } from '../../core/models/user-preference';
import { ActivatedRoute } from '@angular/router';
import { UserPreferencesService } from '../../core/services/user-preference.service';
import { LocationComponent } from '../location/location.component';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-edit-preparer',
imports: [AngularMaterialModule, BasicDetailsComponent, ContactsComponent, LocationComponent],
imports: [AngularMaterialModule, CommonModule, BasicDetailsComponent, ContactsComponent, LocationComponent],
templateUrl: './edit-preparer.component.html',
styleUrl: './edit-preparer.component.scss'
})

View File

@ -1 +1,221 @@
<p>location works!</p>
<div class="locations-container">
<div class="actions-bar">
<button mat-raised-button color="primary" (click)="addNewLocation()">
<mat-icon>add</mat-icon> Add New Location
</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>
<!-- Name Column -->
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Location Name</th>
<td mat-cell *matCellDef="let location">{{ location.name }}</td>
</ng-container>
<!-- Address1 Column -->
<ng-container matColumnDef="address">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Address</th>
<td mat-cell *matCellDef="let location">{{ getAddressLabel(location.address1, location.address2,
location.zip) }}</td>
</ng-container>
<!-- City Column -->
<ng-container matColumnDef="city">
<th mat-header-cell *matHeaderCellDef mat-sort-header>City</th>
<td mat-cell *matCellDef="let location">{{ location.city }}</td>
</ng-container>
<!-- State Column -->
<ng-container matColumnDef="state">
<th mat-header-cell *matHeaderCellDef mat-sort-header>State</th>
<td mat-cell *matCellDef="let location">{{ location.state }}</td>
</ng-container>
<!-- Country Column -->
<ng-container matColumnDef="country">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Country</th>
<td mat-cell *matCellDef="let location">{{ location.country }}</td>
</ng-container>
<!-- Actions Column -->
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef>Actions</th>
<td mat-cell *matCellDef="let location">
<button mat-icon-button color="primary" (click)="editLocation(location)" matTooltip="Edit">
<mat-icon>edit</mat-icon>
</button>
<!--
<button mat-icon-button color="warn" *ngIf="!location.defaultLocation || !location.isInactive"
(click)="
deleteLocation(location.clientLocationid)"
[hidden]="location.defaultLocation || location.isInactive" matTooltip="Inactivate">
<mat-icon>delete</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!]"
[hidePageSize]="true" showFirstLastButtons></mat-paginator>
</div>
<!-- Location Form -->
<div class="form-container" *ngIf="showForm">
<form [formGroup]="locationForm" (ngSubmit)="saveLocation()">
<div class="form-header">
<h3>{{ isEditing ? 'Edit Location' : 'Add New Location' }}</h3>
</div>
<div class="form-row">
<mat-form-field appearance="outline" class="name">
<mat-label>Name</mat-label>
<input matInput formControlName="name" required>
<mat-error *ngIf="locationForm.get('name')?.errors?.['required']">
Name is required
</mat-error>
<mat-error *ngIf="locationForm.get('name')?.errors?.['maxlength']">
Maximum 100 characters allowed
</mat-error>
</mat-form-field>
</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="locationForm.get('address1')?.errors?.['required']">
Address is required
</mat-error>
<mat-error *ngIf="locationForm.get('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="locationForm.get('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="locationForm.get('city')?.errors?.['required']">
City is required
</mat-error>
<mat-error *ngIf="locationForm.get('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="locationForm.get('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="locationForm.get('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="locationForm.get('zip')?.errors?.['required']">
ZIP/Postal code is required
</mat-error>
<mat-error
*ngIf="locationForm.get('country')?.value === 'US' && locationForm.get('zip')?.touched && locationForm.get('zip')?.errors?.['invalidUSZip']">
Please enter a valid 5-digit US ZIP code
</mat-error>
<mat-error
*ngIf="locationForm.get('country')?.value === 'CA' && locationForm.get('zip')?.touched && locationForm.get('zip')?.errors?.['invalidCanadaPostal']">
Please enter a valid postal code (e.g., A1B2C3)
</mat-error>
</mat-form-field>
</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">
{{locationReadOnlyFields.lastChangedBy || 'N/A'}}
</div>
</div>
<!-- Inactive status -->
<div class="readonly-field">
<label>Inactive Status </label>
<div class="readonly-value">
{{locationReadOnlyFields.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">
{{(locationReadOnlyFields.lastChangedDate | date:'mediumDate':'UTC') || 'N/A'}}
</div>
</div>
<!-- Inactivated Date -->
<div class="readonly-field">
<label>Inactivated Date</label>
<div class="readonly-value">
{{(locationReadOnlyFields.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]="locationForm.invalid">
{{ isEditing ? 'Update' : 'Save' }}
</button>
</div>
</form>
</div>
</div>

View File

@ -0,0 +1,163 @@
.locations-container {
padding: 24px;
display: flex;
flex-direction: column;
gap: 24px;
.actions-bar {
clear: both;
margin-bottom: -16px;
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;
}
}
.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;
}
.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.9375rem;
display: flex;
align-items: center;
}
}
}
}
}
}
// Responsive adjustments
@media (max-width: 768px) {
.location-container {
padding: 16px;
.form-row {
flex-direction: column;
gap: 16px !important;
.small-field {
max-width: 100% !important;
}
}
}
}

View File

@ -1,11 +1,267 @@
import { Component } from '@angular/core';
import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core';
import { AngularMaterialModule } from '../../shared/module/angular-material.module';
import { CommonModule } from '@angular/common';
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
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 { Location } from '../../core/models/preparer/location';
import { LocationService } from '../../core/services/preparer/location.service';
import { NotificationService } from '../../core/services/common/notification.service';
import { MatDialog } from '@angular/material/dialog';
import { ApiErrorHandlerService } from '../../core/services/common/api-error-handler.service';
import { CustomPaginator } from '../../shared/custom-paginator';
import { ConfirmDialogComponent } from '../../shared/components/confirm-dialog/confirm-dialog.component';
import { ZipCodeValidator } from '../../shared/validators/zipcode-validator';
import { Country } from '../../core/models/country';
import { Region } from '../../core/models/region';
import { State } from '../../core/models/state';
import { Subject, takeUntil } from 'rxjs';
import { CommonService } from '../../core/services/common/common.service';
@Component({
selector: 'app-location',
imports: [],
imports: [AngularMaterialModule, CommonModule, ReactiveFormsModule],
templateUrl: './location.component.html',
styleUrl: './location.component.scss'
styleUrl: './location.component.scss',
providers: [{ provide: MatPaginatorIntl, useClass: CustomPaginator }],
})
export class LocationComponent {
@ViewChild(MatPaginator) paginator!: MatPaginator;
@ViewChild(MatSort) sort!: MatSort;
displayedColumns: string[] = ['name', 'address', 'city', 'state', 'country', 'actions'];
dataSource = new MatTableDataSource<any>();
locationForm: FormGroup;
isEditing = false;
currentLocationId: number | null = null;
isLoading = false;
showForm = false;
countries: Country[] = [];
states: State[] = [];
locationReadOnlyFields: any = {
lastChangedDate: null,
lastChangedBy: null,
isInactive: null,
inactivatedDate: null
};
@Input() clientid: number = 0;
@Input() userPreferences: UserPreferences = {};
@Output() hasLocations = new EventEmitter<boolean>();
countriesHasStates = ['US', 'CA', 'MX'];
private destroy$ = new Subject<void>();
constructor(
private fb: FormBuilder,
private locationService: LocationService,
private notificationService: NotificationService,
private dialog: MatDialog,
private errorHandler: ApiErrorHandlerService,
private commonService: CommonService
) {
this.locationForm = this.fb.group({
name: ['', [Validators.required, Validators.maxLength(100)]],
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.loadCountries();
this.loadLocations();
}
ngAfterViewInit() {
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
loadLocations(): void {
this.isLoading = true;
this.locationService.getLocationsById(this.clientid).subscribe({
next: (locations: Location[]) => {
this.dataSource.data = locations;
this.isLoading = false;
},
error: (error: any) => {
let errorMessage = this.errorHandler.handleApiError(error, 'Failed to load locations');
this.notificationService.showError(errorMessage);
this.isLoading = false;
console.error('Error loading locations:', error);
}
});
}
onCountryChange(country: string): void {
this.locationForm.get('state')?.reset();
if (country) {
this.loadStates(country);
}
this.locationForm.get('zip')?.updateValueAndValidity();
}
addNewLocation(): void {
this.showForm = true;
this.isEditing = false;
this.currentLocationId = null;
this.locationForm.reset();
}
editLocation(location: Location): void {
this.showForm = true;
this.isEditing = true;
this.currentLocationId = location.locationid;
this.locationForm.patchValue({
name: location.name,
address1: location.address1,
address2: location.address2,
city: location.city,
country: location.country,
state: location.state,
zip: location.zip,
});
if (location.country) {
this.loadStates(location.country);
}
this.locationReadOnlyFields.lastChangedDate = location.lastUpdatedDate ?? location.dateCreated;
this.locationReadOnlyFields.lastChangedBy = location.lastUpdatedBy ?? location.createdBy;
this.locationReadOnlyFields.isInactive = location.isInactive;
this.locationReadOnlyFields.inactivatedDate = location.inactivatedDate;
}
saveLocation(): void {
if (this.locationForm.invalid) {
this.locationForm.markAllAsTouched();
return;
}
// default the first location
const locationData: Location = this.locationForm.value;
const saveObservable = this.isEditing && (this.currentLocationId! > 0)
? this.locationService.updateLocation(this.currentLocationId!, locationData)
: this.locationService.createLocation(this.clientid, locationData);
saveObservable.subscribe({
next: () => {
this.notificationService.showSuccess(`Location ${this.isEditing ? 'updated' : 'added'} successfully`);
this.loadLocations();
this.cancelEdit();
this.hasLocations.emit(true);
},
error: (error) => {
let errorMessage = this.errorHandler.handleApiError(error, `Failed to ${this.isEditing ? 'update' : 'add'} location`);
this.notificationService.showError(errorMessage);
console.error('Error saving location:', error);
}
});
}
loadCountries(): void {
this.commonService.getCountries(this.clientid)
.pipe(takeUntil(this.destroy$))
.subscribe({
next: (countries) => {
this.countries = countries;
},
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.clientid)
.pipe(takeUntil(this.destroy$))
.subscribe({
next: (states) => {
this.states = states;
const stateControl = this.locationForm.get('state');
if (this.countriesHasStates.includes(country)) {
stateControl?.enable();
} else {
stateControl?.disable();
stateControl?.setValue('FN');
}
this.isLoading = false;
},
error: (error) => {
console.error('Failed to load states', error);
this.isLoading = false;
}
});
}
// deleteLocation(locationId: string): void {
// const dialogRef = this.dialog.open(ConfirmDialogComponent, {
// width: '350px',
// data: {
// title: 'Confirm Delete',
// message: 'Are you sure you want to delete this location?',
// confirmText: 'Delete',
// cancelText: 'Cancel'
// }
// });
// dialogRef.afterClosed().subscribe(result => {
// if (result) {
// this.locationService.deleteLocation(locationId).subscribe({
// next: () => {
// this.notificationService.showSuccess('Location deleted successfully');
// this.loadLocations();
// },
// error: (error) => {
// let errorMessage = this.errorHandler.handleApiError(error, 'Failed to delete location');
// this.notificationService.showError(errorMessage);
// console.error('Error deleting location:', error);
// }
// });
// }
// });
// }
getAddressLabel(address1: string, address2?: string, zip?: string): string {
let addressLabel = address1;
if (address2) {
addressLabel += `, ${address2}`;
}
if (zip) {
addressLabel += `, ${zip}`;
}
return addressLabel;
}
getCountryLabel(value: string): string {
const country = this.countries.find(c => c.value === value);
return country ? country.name : value;
}
cancelEdit(): void {
this.showForm = false;
this.isEditing = false;
this.currentLocationId = null;
this.locationForm.reset();
}
}

View File

@ -0,0 +1,133 @@
<div class="manage-preparers-container">
<h2 class="page-header">Manage Preparers</h2>
<div class="search-section">
<form [formGroup]="searchForm" class="search-form" (ngSubmit)="onSearch()">
<div class="search-fields">
<!-- Search Criteria Row 1 -->
<div class="form-row">
<mat-form-field appearance="outline" class="name">
<mat-label>Name</mat-label>
<input matInput formControlName="name" placeholder="Enter company name">
<mat-icon matSuffix>search</mat-icon>
</mat-form-field>
<mat-form-field appearance="outline" class="address">
<mat-label>Address</mat-label>
<input matInput formControlName="address" placeholder="Enter address">
</mat-form-field>
</div>
<!-- Search Criteria Row 2 -->
<div class="form-row">
<mat-form-field appearance="outline" class="city">
<mat-label>City</mat-label>
<input matInput formControlName="city" placeholder="Enter city">
</mat-form-field>
<mat-form-field appearance="outline" class="state">
<mat-label>State</mat-label>
<input matInput formControlName="state" placeholder="Enter state">
</mat-form-field>
<mat-form-field appearance="outline" class="lookup-code">
<mat-label>Lookup Code</mat-label>
<input matInput formControlName="lookupCode" placeholder="Enter lookup code">
</mat-form-field>
</div>
</div>
<div class="search-actions">
<button mat-raised-button color="primary" type="submit"
[disabled]="searchForm.invalid || !isSearchCriteriaProvided()">
<mat-icon>search</mat-icon>
Search
</button>
<button mat-raised-button type="button" (click)="onClear()">
<mat-icon>clear</mat-icon>
Clear Filters
</button>
<button mat-raised-button color="primary" (click)="navigateTo('add-preparer')">
<mat-icon>add</mat-icon>
Add New Preparer
</button>
</div>
</form>
</div>
<div class="results-section" *ngIf="showResults">
<div class="loading-shade" *ngIf="isLoading">
<mat-spinner diameter="50"></mat-spinner>
</div>
<table mat-table matSort [dataSource]="dataSource"
*ngIf="!isLoading && dataSource && dataSource.data.length > 0" class="results-table">
<!-- Name Column -->
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Name</th>
<td mat-cell *matCellDef="let client">{{client.name}}</td>
</ng-container>
<!-- Address Column -->
<ng-container matColumnDef="address">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Address</th>
<td mat-cell *matCellDef="let client">{{ getAddressLabel(client.address1, client.address2, client.zip)}}
</td>
</ng-container>
<!-- City Column -->
<ng-container matColumnDef="city">
<th mat-header-cell *matHeaderCellDef mat-sort-header>City</th>
<td mat-cell *matCellDef="let client">{{client.city}}</td>
</ng-container>
<!-- State Column -->
<ng-container matColumnDef="state">
<th mat-header-cell *matHeaderCellDef mat-sort-header>State</th>
<td mat-cell *matCellDef="let client">{{client.state}}</td>
</ng-container>
<!-- Country Column -->
<ng-container matColumnDef="country">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Country</th>
<td mat-cell *matCellDef="let client">{{client.country}}</td>
</ng-container>
<!-- Carnet Issuing region Column -->
<ng-container matColumnDef="carnetIssuingRegion">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Carnet Issuing region </th>
<td mat-cell *matCellDef="let client">{{getRegionLabel(client.carnetIssuingRegion)}}</td>
</ng-container>
<!-- Revenue Location Column -->
<ng-container matColumnDef="revenueLocation">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Revenue Location</th>
<td mat-cell *matCellDef="let client">{{client.revenueLocation}}</td>
</ng-container>
<!-- Actions Column -->
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef class="actions-header">Actions</th>
<td mat-cell *matCellDef="let client" class="actions-cell">
<button mat-icon-button color="primary" (click)="onEdit(client.clientid)" matTooltip="Edit">
<mat-icon>edit</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 found matching your criteria</span>
</td>
</tr>
</table>
<mat-paginator [length]="dataSource.data.length" [pageSizeOptions]="[userPreferences.pageSize!]"
[hidePageSize]="true" showFirstLastButtons></mat-paginator>
</div>
</div>

View File

@ -0,0 +1,128 @@
.page-header {
margin: 0.5rem 0px;
color: var(--mat-sys-primary);
font-weight: 500;
}
.manage-preparers-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,
.address {
grid-column: span 2;
}
.city,
.state,
.lookup-code {
grid-column: span 1;
}
}
}
.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;
}
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;
}
}
}
@media (max-width: 960px) {
.manage-preparers-container {
.search-form {
.search-fields {
.form-row {
grid-template-columns: 1fr;
.name,
.address,
.city,
.state,
.lookup-code {
grid-column: span 1;
}
}
}
}
}
}

View File

@ -0,0 +1,183 @@
import { AfterViewInit, Component, OnDestroy, OnInit, 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 { MatPaginator, MatPaginatorIntl } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { Subject, takeUntil } from 'rxjs';
import { BasicDetail } from '../../core/models/preparer/basic-detail';
import { NotificationService } from '../../core/services/common/notification.service';
import { NavigationService } from '../../core/services/common/navigation.service';
import { UserPreferencesService } from '../../core/services/user-preference.service';
import { UserPreferences } from '../../core/models/user-preference';
import { ClientService } from '../../core/services/preparer/client.service';
import { PreparerFilter } from '../../core/models/preparer/preparer-filter';
import { ApiErrorHandlerService } from '../../core/services/common/api-error-handler.service';
import { CustomPaginator } from '../../shared/custom-paginator';
import { CommonService } from '../../core/services/common/common.service';
import { Region } from '../../core/models/region';
import { Country } from '../../core/models/country';
@Component({
selector: 'app-manage',
imports: [AngularMaterialModule, ReactiveFormsModule, CommonModule],
templateUrl: './manage-preparer.component.html',
styleUrl: './manage-preparer.component.scss',
providers: [{ provide: MatPaginatorIntl, useClass: CustomPaginator }]
})
export class ManagePreparerComponent implements OnInit, OnDestroy, AfterViewInit {
@ViewChild(MatPaginator, { static: false })
set paginator(value: MatPaginator) {
this.dataSource.paginator = value;
}
@ViewChild(MatSort, { static: false })
set sort(value: MatSort) {
this.dataSource.sort = value;
}
searchForm: FormGroup;
showResults = false;
isLoading = false;
userPreferences: UserPreferences;
countries: Country[] = [];
regions: Region[] = [];
displayedColumns: string[] = ['name', 'address', 'city', 'state', 'country', 'carnetIssuingRegion', 'revenueLocation', 'actions'];
dataSource = new MatTableDataSource<any>([]);
private destroy$ = new Subject<void>();
constructor(
private fb: FormBuilder,
private notificationService: NotificationService,
private userPrefenceService: UserPreferencesService,
private navigationService: NavigationService,
private clientService: ClientService,
private errorHandler: ApiErrorHandlerService,
private commonService: CommonService
) {
this.userPreferences = userPrefenceService.getPreferences();
this.searchForm = this.createSearchForm();
}
ngOnInit(): void {
this.loadCountries();
this.loadRegions();
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
ngAfterViewInit() {
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
}
createSearchForm(): FormGroup {
return this.fb.group({
name: [''],
address: [''],
city: [''],
state: [''],
lookupCode: ['']
});
}
isSearchCriteriaProvided(): boolean {
const values = this.searchForm.value;
return !!values.name || !!values.address || !!values.city || !!values.state || !!values.lookupCode;
}
onSearch(): void {
if (this.searchForm.invalid || !this.isSearchCriteriaProvided()) {
return;
}
this.isLoading = true;
this.showResults = true;
const filterData: PreparerFilter = this.searchForm.value;
this.clientService.getPreparers(filterData).subscribe({
next: (clients: BasicDetail[]) => {
this.dataSource.data = clients;
this.isLoading = false;
},
error: (error: any) => {
let errorMessage = this.errorHandler.handleApiError(error, 'Failed to search preparers');
this.notificationService.showError(errorMessage);
this.isLoading = false;
console.error('Error loading preparers:', error);
}
});
}
onClear(): void {
this.searchForm.reset();
this.showResults = false;
this.dataSource.data = [];
}
navigateTo(route: string): void {
this.navigationService.navigate([route]);
}
onEdit(clientid: number): void {
this.navigationService.navigate(['preparer', clientid]);
}
loadRegions(): void {
this.commonService.getRegions()
.pipe(takeUntil(this.destroy$))
.subscribe({
next: (regions) => {
this.regions = regions;
this.isLoading = false;
},
error: (error) => {
console.error('Failed to load regions', error);
this.isLoading = false;
}
});
}
loadCountries(): void {
this.commonService.getCountries(0)
.pipe(takeUntil(this.destroy$))
.subscribe({
next: (countries) => {
this.countries = countries;
},
error: (error) => {
console.error('Failed to load countries', error);
this.isLoading = false;
}
});
}
getAddressLabel(address1: string, address2?: string, zip?: string): string {
let addressLabel = address1;
if (address2) {
addressLabel += `, ${address2}`;
}
if (zip) {
addressLabel += `, ${zip}`;
}
return addressLabel;
}
getRegionLabel(value: string): string {
const region = this.regions.find(r => r.region === value);
return region ? region.regionname : value;
}
getCountryLabel(value: string): string {
const country = this.countries.find(c => c.value === value);
return country ? country.name : value;
}
}

View File

@ -24,6 +24,7 @@ import { MatToolbarModule } from '@angular/material/toolbar';
import { MatTooltipModule } from '@angular/material/tooltip';
import { MatAccordion, MatExpansionModule } from '@angular/material/expansion';
import { MatStepperModule } from '@angular/material/stepper';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MAT_MOMENT_DATE_ADAPTER_OPTIONS, MatMomentDateModule } from '@angular/material-moment-adapter';
@NgModule({
@ -53,7 +54,8 @@ import { MAT_MOMENT_DATE_ADAPTER_OPTIONS, MatMomentDateModule } from '@angular/m
MatExpansionModule,
MatAccordion,
MatStepperModule,
MatMomentDateModule
MatMomentDateModule,
MatSlideToggleModule
],
exports: [
MatButtonModule,
@ -79,7 +81,8 @@ import { MAT_MOMENT_DATE_ADAPTER_OPTIONS, MatMomentDateModule } from '@angular/m
MatExpansionModule,
MatAccordion,
MatStepperModule,
MatMomentDateModule
MatMomentDateModule,
MatSlideToggleModule
],
providers: [
{ provide: MAT_MOMENT_DATE_ADAPTER_OPTIONS, useValue: { useUtc: true } }