region updates

This commit is contained in:
Cyril Joseph 2025-10-28 22:21:55 -03:00
parent 3e7ae6b70b
commit bd619d306f
11 changed files with 559 additions and 6 deletions

View File

@ -11,6 +11,7 @@ import { ForgotPasswordComponent } from './forgot-password/forgot-password.compo
import { ParamTableComponent } from './param/table/param-table.component'; import { ParamTableComponent } from './param/table/param-table.component';
import { ManageTableRecordComponent } from './param/manage-table-record/manage-table-record.component'; import { ManageTableRecordComponent } from './param/manage-table-record/manage-table-record.component';
import { ManageParamRecordComponent } from './param/manage-param-record/manage-param-record.component'; import { ManageParamRecordComponent } from './param/manage-param-record/manage-param-record.component';
import { RegionComponent } from './region/region.component';
export const routes: Routes = [ export const routes: Routes = [
{ path: 'login', component: LoginComponent }, { path: 'login', component: LoginComponent },
@ -18,6 +19,7 @@ export const routes: Routes = [
{ path: 'forgot-password', component: ForgotPasswordComponent }, { path: 'forgot-password', component: ForgotPasswordComponent },
{ path: 'table-record', component: ManageTableRecordComponent }, { path: 'table-record', component: ManageTableRecordComponent },
{ path: 'param-record/:id', component: ManageParamRecordComponent }, { path: 'param-record/:id', component: ManageParamRecordComponent },
{ path: 'region', component: RegionComponent, canActivate: [AuthGuard] },
{ path: 'usersettings', component: UserSettingsComponent, canActivate: [AuthGuard] }, { path: 'usersettings', component: UserSettingsComponent, canActivate: [AuthGuard] },
{ path: 'home', component: HomeComponent, canActivate: [AuthGuard] }, { path: 'home', component: HomeComponent, canActivate: [AuthGuard] },
{ path: 'service-provider/:id', component: EditServiceProviderComponent, canActivate: [AuthGuard] }, { path: 'service-provider/:id', component: EditServiceProviderComponent, canActivate: [AuthGuard] },

View File

@ -6,8 +6,11 @@
<div class="navbar-menu"> <div class="navbar-menu">
<button mat-button (click)="navigateTo('home')">Home</button> <button mat-button (click)="navigateTo('home')">Home</button>
<button mat-button (click)="navigateTo('/table-record')">Configurations</button> <button mat-button [matMenuTriggerFor]="configurations">Configurations</button>
<mat-menu #configurations="matMenu">
<button mat-menu-item (click)="navigateTo('table-record')">Table Records</button>
<button mat-menu-item (click)="navigateTo('region')">Manage Region</button>
</mat-menu>
<div class="profile-container"> <div class="profile-container">
<button mat-icon-button (click)="toggleProfileMenu()" class="profile-button"> <button mat-icon-button (click)="toggleProfileMenu()" class="profile-button">
<mat-icon>account_circle</mat-icon> <mat-icon>account_circle</mat-icon>

View File

@ -0,0 +1,5 @@
export interface Region {
id: number;
value: string;
name: string;
}

View File

@ -0,0 +1,77 @@
import { inject, Injectable } from '@angular/core';
import { environment } from '../../../../environments/environment';
import { HttpClient } from '@angular/common/http';
import { map, Observable } from 'rxjs';
// import { UserService } from '../common/user.service';
// import { CommonService } from '../common/common.service';
import { Region } from '../../models/region/region';
@Injectable({
providedIn: 'root'
})
export class RegionService {
private apiUrl = environment.apiUrl;
private apiDb = environment.apiDb;
private http = inject(HttpClient);
// private userService = inject(UserService);
// private commonService = inject(CommonService);
getRegions(spid: number = 0): Observable<Region[]> {
return this.http.get<any[]>(`${this.apiUrl}/${this.apiDb}/GetRegions/${spid}`).pipe(
map(response => this.mapToRegions(response)));
}
private mapToRegions(data: any[]): Region[] {
return data.map(item => ({
id: item.REGIONID,
value: item.REGION,
name: item.REGIONNAME,
// spid: item.SPID
}));
}
// getRegionById(regionId: number): Observable<Region> {
// return this.http.get<any>(`${this.apiUrl}/${this.apiDb}/GetRegion/${regionId}`).pipe(
// map(response => this.mapToRegion(response)));
// }
// private mapToRegion(data: any): Region {
// return {
// id: data.ID,
// Value: data.VALUE,
// Name: data.NAME,
// P_SPID: data.P_SPID
// };
// }
createRegion(region: Region, spid: number = 0): Observable<any> {
const payload = {
P_REGION: region.value,
P_NAME: region.name,
P_SPID: spid,
// P_USERID: this.userService.getUser()
};
return this.http.post<any>(`${this.apiUrl}/${this.apiDb}/InsertRegions`, payload);
}
updateRegion(regionId: number, region: Region): Observable<any> {
const payload = {
P_REGIONID: regionId,
P_NAME: region.name,
// P_USERID: this.userService.getUser()
};
return this.http.patch<any>(`${this.apiUrl}/${this.apiDb}/UpdateRegion`, payload);
}
// deleteRegion(regionId: number): Observable<any> {
// const payload = {
// P_REGIONID: regionId,
// P_USERID: this.userService.getUser()
// };
// return this.http.delete<any>(`${this.apiUrl}/${this.apiDb}/DeleteRegion`, { body: payload });
// }
}

View File

@ -0,0 +1,111 @@
<div class="region-container">
<div class="actions-bar">
<!-- <mat-slide-toggle (change)="toggleShowExpiredRecords()">
Show Expired Records
</mat-slide-toggle> -->
<button mat-raised-button color="primary" (click)="addNewRegion()">
<mat-icon>add</mat-icon> Add New Region
</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>
<!-- Value Column -->
<ng-container matColumnDef="value">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Region Value</th>
<td mat-cell *matCellDef="let item">{{ item.value }}</td>
</ng-container>
<!-- Name Column -->
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Region Name</th>
<td mat-cell *matCellDef="let item">{{ item.name }}</td>
</ng-container>
<!-- Actions Column -->
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef>Actions</th>
<td mat-cell *matCellDef="let item">
<button mat-icon-button color="primary" (click)="editRegion(item)" 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 regions available</span>
</td>
</tr>
</table>
<mat-paginator *ngIf="dataSource.data.length > userPreferences.pageSize!" [length]="dataSource.data.length"
[pageSizeOptions]="[userPreferences.pageSize!]" [hidePageSize]="true" showFirstLastButtons></mat-paginator>
</div>
<!-- Region Form -->
<div class="form-container" *ngIf="showForm">
<form [formGroup]="regionForm" (ngSubmit)="saveRegion()">
<div class="form-header">
<h3>{{ isEditing ? 'Edit Region' : 'Add New Region' }}</h3>
</div>
<div class="form-row">
<mat-form-field appearance="outline">
<mat-label>Region</mat-label>
<input matInput formControlName="value" required [readonly]="isEditing">
<mat-error *ngIf="regionForm.get('value')?.errors?.['required']">
Region is required
</mat-error>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Region Name</mat-label>
<input matInput formControlName="name" required>
<mat-error *ngIf="regionForm.get('name')?.errors?.['required']">
Region Name is required
</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">
{{readOnlyFields.lastChangedBy || 'N/A'}}
</div>
</div>
</div>
<div class="field-column"> -->
<!-- Last Changed Date -->
<!-- <div class="readonly-field">
<label>Last Changed Date</label>
<div class="readonly-value">
{{(readOnlyFields.lastChangedDate | date:'mediumDate':'UTC') || 'N/A'}}
</div>
</div>
</div>
</div>
</div> -->
<div class="form-actions">
<button mat-raised-button color="primary" type="submit"
[disabled]="regionForm.invalid || changeInProgress">
{{ isEditing ? 'Update' : 'Save' }}
</button>
<button mat-button type="button" (click)="cancelEdit()">Cancel</button>
</div>
</form>
</div>
</div>

View File

@ -0,0 +1,159 @@
.region-container {
padding: 0.5rem;
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: 120px;
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 {
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;
}
}
.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;
}
}
}
}
}
}
// Responsive adjustments
@media (max-width: 768px) {
.region-container {
padding: 16px;
.form-row {
flex-direction: column;
gap: 16px !important;
}
}
}

View File

@ -0,0 +1,175 @@
import { Component, EventEmitter, inject, Input, OnInit, Output, ViewChild } from '@angular/core';
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 { AngularMaterialModule } from '../shared/module/angular-material.module';
import { CommonModule } from '@angular/common';
import { Region } from '../core/models/region/region';
import { CustomPaginator } from '../shared/custom-paginator';
import { UserPreferences } from '../core/models/user-preference';
import { ApiErrorHandlerService } from '../core/services/common/api-error-handler.service';
import { NotificationService } from '../core/services/common/notification.service';
import { RegionService } from '../core/services/region/region.service';
import { UserPreferencesService } from '../core/services/user-preference.service';
import { finalize } from 'rxjs';
@Component({
selector: 'app-region',
imports: [AngularMaterialModule, CommonModule, ReactiveFormsModule],
templateUrl: './region.component.html',
styleUrl: './region.component.scss',
providers: [{ provide: MatPaginatorIntl, useClass: CustomPaginator }],
})
export class RegionComponent implements OnInit {
@ViewChild(MatPaginator) paginator!: MatPaginator;
@ViewChild(MatSort) sort!: MatSort;
displayedColumns: string[] = ['value', 'name', 'actions'];
dataSource = new MatTableDataSource<any>();
regionForm: FormGroup;
isEditing = false;
currentRegionId: number | null = null;
isLoading = false;
changeInProgress = false;
showForm = false;
showExpiredRecords = false;
regions: Region[] = [];
userPreferences!: UserPreferences;
// readOnlyFields: any = {
// lastChangedDate: null,
// lastChangedBy: null
// };
@Input() isEditMode = false;
@Input() spid: number = 0;
@Output() hasRegions = new EventEmitter<boolean>();
private fb = inject(FormBuilder);
private regionService = inject(RegionService);
private notificationService = inject(NotificationService);
private errorHandler = inject(ApiErrorHandlerService);
constructor(userPrefenceService: UserPreferencesService) {
this.userPreferences = userPrefenceService.getPreferences();
this.regionForm = this.fb.group({
value: ['', Validators.required],
name: ['', Validators.required]
});
}
ngOnInit(): void {
this.loadRegions();
}
ngAfterViewInit() {
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
}
loadRegions(): void {
this.isLoading = true;
this.regionService.getRegions(this.spid).pipe(finalize(() => {
this.isLoading = false;
}))
.subscribe({
next: (regions: Region[]) => {
this.regions = regions;
this.dataSource.data = this.regions;
// this.renderRecords();
},
error: (error: any) => {
let errorMessage = this.errorHandler.handleApiError(error, 'Failed to load regions');
this.notificationService.showError(errorMessage);
console.error('Error loading regions:', error);
}
});
}
// toggleShowExpiredRecords(): void {
// this.showExpiredRecords = !this.showExpiredRecords;
// this.renderRecords();
// }
// renderRecords(): void {
// // if (this.showExpiredRecords) {
// // this.dataSource.data = this.regions;
// // } else {
// // this.dataSource.data = this.regions; // Adjust if you have expired logic
// //}
// }
addNewRegion(): void {
this.showForm = true;
this.isEditing = false;
this.currentRegionId = null;
this.regionForm.reset({
value: '',
name: ''
});
this.regionForm.get('value')?.enable();
this.regionForm.get('name')?.enable();
}
editRegion(region: Region): void {
this.showForm = true;
this.isEditing = true;
this.currentRegionId = region.id;
this.regionForm.patchValue({
value: region.value,
name: region.name
});
// // Set readonly fields if available
// this.readOnlyFields.lastChangedDate = null; // Update based on your API response
// this.readOnlyFields.lastChangedBy = null; // Update based on your API response
this.regionForm.get('value')?.disable();
}
saveRegion(): void {
if (this.regionForm.invalid) {
this.regionForm.markAllAsTouched();
return;
}
const regionData: Region = {
id: this.currentRegionId || 0,
value: this.regionForm.get('value')?.value,
name: this.regionForm.get('name')?.value
};
const saveObservable = this.isEditing && this.currentRegionId
? this.regionService.updateRegion(this.currentRegionId, regionData)
: this.regionService.createRegion(regionData, this.spid);
this.changeInProgress = true;
saveObservable.pipe(finalize(() => {
this.changeInProgress = false;
})).subscribe({
next: () => {
this.notificationService.showSuccess(`Region ${this.isEditing ? 'updated' : 'added'} successfully`);
this.loadRegions();
this.cancelEdit();
this.hasRegions.emit(true);
},
error: (error) => {
let errorMessage = this.errorHandler.handleApiError(error, `Failed to ${this.isEditing ? 'update' : 'add'} region`);
this.notificationService.showError(errorMessage);
console.error('Error saving region:', error);
}
});
}
cancelEdit(): void {
this.showForm = false;
this.isEditing = false;
this.currentRegionId = null;
this.regionForm.reset();
this.regionForm.get('value')?.enable();
this.regionForm.get('name')?.enable();
}
}

View File

@ -19,6 +19,14 @@
</app-contacts> </app-contacts>
</mat-step> </mat-step>
<!-- Regions Step -->
<mat-step [completed]="regionCompleted" [editable]="!!serviceProviderId && basicDetailsCompleted">
<ng-template matStepLabel>Regions</ng-template>
<app-region *ngIf="serviceProviderId" [spid]="serviceProviderId" (hasRegions)="onRegionsSaved($event)">
</app-region>
</mat-step>
<!-- Carnet Sequence Step --> <!-- Carnet Sequence Step -->
<mat-step [editable]="!!serviceProviderId && basicDetailsCompleted" [completed]="carnetSequenceCompleted"> <mat-step [editable]="!!serviceProviderId && basicDetailsCompleted" [completed]="carnetSequenceCompleted">
<ng-template matStepLabel>Carnet Sequence</ng-template> <ng-template matStepLabel>Carnet Sequence</ng-template>
@ -56,8 +64,7 @@
</mat-step> </mat-step>
<!-- Continuation Sheet Fee Step --> <!-- Continuation Sheet Fee Step -->
<mat-step [editable]="!!serviceProviderId && basicDetailsCompleted" <mat-step [editable]="!!serviceProviderId && basicDetailsCompleted" [completed]="continuationSheetFeeCompleted">
[completed]="continuationSheetFeeCompleted">
<ng-template matStepLabel>Continuation Sheet Fee Setup</ng-template> <ng-template matStepLabel>Continuation Sheet Fee Setup</ng-template>
<app-continuation-sheet-fee *ngIf="serviceProviderId" [spid]="serviceProviderId" <app-continuation-sheet-fee *ngIf="serviceProviderId" [spid]="serviceProviderId"

View File

@ -14,11 +14,12 @@ import { ContinuationSheetFeeComponent } from "../continuation-sheet-fee/continu
import { UserPreferences } from '../../core/models/user-preference'; import { UserPreferences } from '../../core/models/user-preference';
import { UserPreferencesService } from '../../core/services/user-preference.service'; import { UserPreferencesService } from '../../core/services/user-preference.service';
import { CargoRateComponent } from '../cargo-rate/cargo-rate.component'; import { CargoRateComponent } from '../cargo-rate/cargo-rate.component';
import { RegionComponent } from '../../region/region.component';
@Component({ @Component({
selector: 'app-add-service-provider', selector: 'app-add-service-provider',
imports: [BasicDetailsComponent, AngularMaterialModule, CommonModule, ContactsComponent, CarnetSequenceComponent, imports: [BasicDetailsComponent, AngularMaterialModule, CommonModule, ContactsComponent, CarnetSequenceComponent,
CarnetFeeComponent, BasicFeeComponent, CounterfoilFeeComponent, ExpeditedFeeComponent, SecurityDepositComponent, ContinuationSheetFeeComponent, CargoRateComponent], CarnetFeeComponent, BasicFeeComponent, CounterfoilFeeComponent, ExpeditedFeeComponent, SecurityDepositComponent, ContinuationSheetFeeComponent, CargoRateComponent, RegionComponent],
templateUrl: './add-service-provider.component.html', templateUrl: './add-service-provider.component.html',
styleUrl: './add-service-provider.component.scss' styleUrl: './add-service-provider.component.scss'
}) })
@ -40,6 +41,7 @@ export class AddServiceProviderComponent {
expeditedFeeCompleted: boolean = false; expeditedFeeCompleted: boolean = false;
securityDepositCompleted: boolean = false; securityDepositCompleted: boolean = false;
cargoRateCompleted: boolean = false; cargoRateCompleted: boolean = false;
regionCompleted: boolean = false;
constructor(userPrefenceService: UserPreferencesService) { constructor(userPrefenceService: UserPreferencesService) {
this.userPreferences = userPrefenceService.getPreferences(); this.userPreferences = userPrefenceService.getPreferences();
@ -87,6 +89,10 @@ export class AddServiceProviderComponent {
this.cargoRateCompleted = event; this.cargoRateCompleted = event;
} }
onRegionsSaved(event: boolean): void {
this.regionCompleted = event;
}
onStepChange(event: StepperSelectionEvent): void { onStepChange(event: StepperSelectionEvent): void {
this.currentStep = event.selectedIndex; this.currentStep = event.selectedIndex;
} }

View File

@ -20,6 +20,13 @@
<app-contacts [spid]="spid" [userPreferences]="userPreferences"></app-contacts> <app-contacts [spid]="spid" [userPreferences]="userPreferences"></app-contacts>
</mat-expansion-panel> </mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title> Regions </mat-panel-title>
</mat-expansion-panel-header>
<app-region [spid]="spid"></app-region>
</mat-expansion-panel>
<mat-expansion-panel> <mat-expansion-panel>
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title> Carnet Sequence </mat-panel-title> <mat-panel-title> Carnet Sequence </mat-panel-title>

View File

@ -15,10 +15,11 @@ import { UserPreferences } from '../../core/models/user-preference';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { UserPreferencesService } from '../../core/services/user-preference.service'; import { UserPreferencesService } from '../../core/services/user-preference.service';
import { CargoRateComponent } from '../cargo-rate/cargo-rate.component'; import { CargoRateComponent } from '../cargo-rate/cargo-rate.component';
import { RegionComponent } from '../../region/region.component';
@Component({ @Component({
selector: 'app-edit-service-provider', selector: 'app-edit-service-provider',
imports: [AngularMaterialModule, BasicDetailsComponent, ContactsComponent, CarnetSequenceComponent, CarnetFeeComponent, BasicFeeComponent, CounterfoilFeeComponent, ContinuationSheetFeeComponent, ExpeditedFeeComponent, SecurityDepositComponent, CommonModule, CargoRateComponent], imports: [AngularMaterialModule, BasicDetailsComponent, ContactsComponent, CarnetSequenceComponent, CarnetFeeComponent, BasicFeeComponent, CounterfoilFeeComponent, ContinuationSheetFeeComponent, ExpeditedFeeComponent, SecurityDepositComponent, CommonModule, CargoRateComponent, RegionComponent],
templateUrl: './edit-service-provider.component.html', templateUrl: './edit-service-provider.component.html',
styleUrl: './edit-service-provider.component.scss' styleUrl: './edit-service-provider.component.scss'
}) })