diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 65404b5..762f8c5 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -11,6 +11,7 @@ import { ForgotPasswordComponent } from './forgot-password/forgot-password.compo import { ParamTableComponent } from './param/table/param-table.component'; import { ManageTableRecordComponent } from './param/manage-table-record/manage-table-record.component'; import { ManageParamRecordComponent } from './param/manage-param-record/manage-param-record.component'; +import { RegionComponent } from './region/region.component'; export const routes: Routes = [ { path: 'login', component: LoginComponent }, @@ -18,6 +19,7 @@ export const routes: Routes = [ { path: 'forgot-password', component: ForgotPasswordComponent }, { path: 'table-record', component: ManageTableRecordComponent }, { path: 'param-record/:id', component: ManageParamRecordComponent }, + { path: 'region', component: RegionComponent, canActivate: [AuthGuard] }, { path: 'usersettings', component: UserSettingsComponent, canActivate: [AuthGuard] }, { path: 'home', component: HomeComponent, canActivate: [AuthGuard] }, { path: 'service-provider/:id', component: EditServiceProviderComponent, canActivate: [AuthGuard] }, diff --git a/src/app/common/secured-header/secured-header.component.html b/src/app/common/secured-header/secured-header.component.html index 4574b83..0845bca 100644 --- a/src/app/common/secured-header/secured-header.component.html +++ b/src/app/common/secured-header/secured-header.component.html @@ -6,8 +6,11 @@ \ No newline at end of file diff --git a/src/app/region/region.component.scss b/src/app/region/region.component.scss new file mode 100644 index 0000000..af6bc77 --- /dev/null +++ b/src/app/region/region.component.scss @@ -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; + } + } +} \ No newline at end of file diff --git a/src/app/region/region.component.ts b/src/app/region/region.component.ts new file mode 100644 index 0000000..81c1d17 --- /dev/null +++ b/src/app/region/region.component.ts @@ -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(); + 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(); + + 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(); + } +} \ No newline at end of file diff --git a/src/app/service-provider/add/add-service-provider.component.html b/src/app/service-provider/add/add-service-provider.component.html index 726a4b5..af045a7 100644 --- a/src/app/service-provider/add/add-service-provider.component.html +++ b/src/app/service-provider/add/add-service-provider.component.html @@ -19,6 +19,14 @@ + + + Regions + + + + + Carnet Sequence @@ -56,8 +64,7 @@ - + Continuation Sheet Fee Setup + + + Regions + + + + Carnet Sequence diff --git a/src/app/service-provider/edit/edit-service-provider.component.ts b/src/app/service-provider/edit/edit-service-provider.component.ts index 6f8c720..4ba3ada 100644 --- a/src/app/service-provider/edit/edit-service-provider.component.ts +++ b/src/app/service-provider/edit/edit-service-provider.component.ts @@ -15,10 +15,11 @@ import { UserPreferences } from '../../core/models/user-preference'; import { CommonModule } from '@angular/common'; import { UserPreferencesService } from '../../core/services/user-preference.service'; import { CargoRateComponent } from '../cargo-rate/cargo-rate.component'; +import { RegionComponent } from '../../region/region.component'; @Component({ 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', styleUrl: './edit-service-provider.component.scss' })