diff --git a/src/app/features/global-masters/cities/pages/city-list/city-list.ts b/src/app/features/global-masters/cities/pages/city-list/city-list.ts
index fbc242b1..74a83561 100644
--- a/src/app/features/global-masters/cities/pages/city-list/city-list.ts
+++ b/src/app/features/global-masters/cities/pages/city-list/city-list.ts
@@ -1,13 +1,11 @@
import { Component, DestroyRef, OnInit, inject, signal, viewChild } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
-import { of } from 'rxjs';
-import { catchError, finalize } from 'rxjs/operators';
+import { Subject, of } from 'rxjs';
+import { debounceTime, distinctUntilChanged, finalize, map, switchMap } from 'rxjs/operators';
import { NotificationService } from '../../../../../core/services/common/notification.service';
import { CityDto, UpdateCityRequest } from '../../models/city.model';
-import { CountryLookupDto } from '../../../countries/models/country.model';
-import { StateLookupDto } from '../../../states/models/state.model';
import { CityService } from '../../data-access/city.service';
import { CountryService } from '../../../countries/data-access/country.service';
import { StateService } from '../../../states/data-access/state.service';
@@ -19,12 +17,8 @@ import {
DataTableColumn,
DataTableRecord
} from '../../../../../shared/components/data-table/data-table.types';
-import { Autocomplete } from '../../../../../shared/components/form/autocomplete/autocomplete';
-import {
- AutocompleteDisplayFn,
- AutocompleteSearchFn,
- AutocompleteValueFn
-} from '../../../../../shared/components/form/autocomplete/autocomplete.types';
+import { FormSelect } from '../../../../../shared/components/form/form-select/form-select';
+import { FormSelectOption } from '../../../../../shared/components/form/models/form-select.models';
import { Button } from '../../../../../shared/components/button/button';
import { ConfirmDialog } from '../../../../../shared/components/confirm-dialog/confirm-dialog';
import { CityFormModalComponent } from '../../components/city-form-modal/city-form-modal';
@@ -52,7 +46,7 @@ interface CityTableRow extends DataTableRecord {
DataTable,
DataTableToolbarDirective,
ReactiveFormsModule,
- Autocomplete,
+ FormSelect,
Button,
ConfirmDialog,
CityFormModalComponent
@@ -70,8 +64,6 @@ export class CityList implements OnInit {
private readonly notification = inject(NotificationService);
readonly tableStore = inject(DataTableStore
);
- readonly selectedCountry = signal(null);
- readonly selectedFilterState = signal(null);
readonly statusChangingId = signal(null);
readonly deletingId = signal(null);
readonly pendingDeleteCity = signal(null);
@@ -80,18 +72,25 @@ export class CityList implements OnInit {
readonly showFilters = signal(false);
readonly filterForm = this.formBuilder.nonNullable.group({
- countryId: [''],
- stateId: [{ value: '', disabled: true }]
+ countryId: [[] as string[]],
+ stateId: [{ value: [] as string[], disabled: true }]
});
+ readonly countryOptions = signal[]>([]);
+ readonly countryOptionsLoading = signal(false);
+ readonly stateOptions = signal[]>([]);
+ readonly stateOptionsLoading = signal(false);
+ private readonly countrySearch$ = new Subject();
+ private readonly stateSearch$ = new Subject();
+
readonly columns = signal[]>([
- { key: 'serialNumber', label: 'Sr. No.', header: 'Sr.No.', sortable: false, width: '90px' },
- { key: 'name', label: 'City Name', header: 'City Name', sortable: true, headerAlign: 'center', align: 'left' },
- { key: 'code', label: 'Code', header: 'Code', sortable: true, headerAlign: 'center', align: 'center', badge: true, badgeClass: value => value ? 'badge bg-primary/10 text-primary' : 'badge bg-secondary/10 text-secondary' },
- { key: 'stateName', label: 'State', header: 'State', sortable: true, headerAlign: 'center', align: 'left' },
- { key: 'countryName', label: 'Country', header: 'Country', sortable: true, headerAlign: 'center', align: 'center' },
+ { key: 'serialNumber', label: 'Sr. No.', header: 'Sr.No.', sortable: false, width: '8%' },
+ { key: 'name', label: 'City Name', header: 'City Name', sortable: true, headerAlign: 'left', align: 'left', width: '30%' },
+ //{ key: 'code', label: 'Code', header: 'Code', sortable: true, headerAlign: 'center', align: 'center', badge: true, badgeClass: value => value ? 'badge bg-primary/10 text-primary' : 'badge bg-secondary/10 text-secondary' },
+ { key: 'stateName', label: 'State', header: 'State', sortable: true, headerAlign: 'left', align: 'left', width: '22%' },
+ { key: 'countryName', label: 'Country', header: 'Country', sortable: true, headerAlign: 'left', align: 'left', width: '22%' },
{
- key: 'isActive', label: 'Status', header: 'Status', sortable: true, badge: true,
+ key: 'isActive', label: 'Status', header: 'Status', sortable: true, badge: true, headerAlign: 'left', align: 'left', width: '18%',
badgeClass: value => value === true ? 'badge bg-success/10 text-success' : 'badge bg-danger/10 text-danger',
formatter: value => value ? 'Active' : 'Inactive'
}
@@ -113,25 +112,57 @@ export class CityList implements OnInit {
}
]);
- readonly filterCountrySearchFn: AutocompleteSearchFn = (term, page) =>
- this.countryApi.autocomplete(term, page);
- readonly filterCountryValueFn: AutocompleteValueFn = c => c.id;
- readonly filterCountryDisplayFn: AutocompleteDisplayFn = c => c.name;
-
- readonly filterStateSearchFn: AutocompleteSearchFn = (term, page) => {
- const countryId = this.filterForm.controls.countryId.value || this.selectedCountry()?.id || '';
- if (!countryId) return of([]);
- return this.stateApi.autocomplete(countryId, term || '', page);
- };
- readonly filterStateValueFn: AutocompleteValueFn = s => s.id;
- readonly filterStateDisplayFn: AutocompleteDisplayFn = s => s.name;
-
ngOnInit(): void {
+ this.countrySearch$.pipe(
+ debounceTime(300),
+ distinctUntilChanged(),
+ switchMap(term => {
+ this.countryOptionsLoading.set(true);
+ return this.countryApi.autocomplete(term, 50);
+ }),
+ takeUntilDestroyed(this.destroyRef)
+ ).subscribe(countries => {
+ this.countryOptionsLoading.set(false);
+ this.countryOptions.set(countries.map(country => ({ value: country.id, label: country.name })));
+ });
+ this.countrySearch$.next('');
+
+ this.stateSearch$.pipe(
+ debounceTime(300),
+ map(term => ({ term, countryIds: this.filterForm.controls.countryId.value })),
+ distinctUntilChanged((previous, current) =>
+ previous.term === current.term && previous.countryIds.join(',') === current.countryIds.join(',')
+ ),
+ switchMap(({ term, countryIds }) => {
+ if (!countryIds.length) return of([]);
+ this.stateOptionsLoading.set(true);
+ return this.stateApi.autocomplete(countryIds, term, 50);
+ }),
+ takeUntilDestroyed(this.destroyRef)
+ ).subscribe(states => {
+ this.stateOptionsLoading.set(false);
+ this.stateOptions.set(states.map(state => ({ value: state.id, label: state.name })));
+ });
+
+ this.filterForm.controls.countryId.valueChanges.pipe(
+ takeUntilDestroyed(this.destroyRef)
+ ).subscribe(countryIds => {
+ this.filterForm.controls.stateId.setValue([], { emitEvent: false });
+ this.stateOptions.set([]);
+ if (countryIds.length) {
+ this.filterForm.controls.stateId.enable({ emitEvent: false });
+ this.stateSearch$.next('');
+ } else {
+ this.filterForm.controls.stateId.disable({ emitEvent: false });
+ }
+ });
+
this.tableStore.initialize({
+ defaultSort: { column: 'name' },
fetcher: query => {
- const countryId = this.filterForm.controls.countryId.value || null;
- const stateId = this.filterForm.controls.stateId.value || null;
- return this.cityApi.getCityDataTable(query, countryId, stateId);
+ const countryIds = this.filterForm.controls.countryId.value;
+ const stateIds = this.filterForm.controls.stateId.value;
+ return this.cityApi.getCityDataTable(query, countryIds, stateIds);
},
mapRow: (city, serialNumber) => {
const resolvedStateName = city.stateName
@@ -160,20 +191,12 @@ export class CityList implements OnInit {
});
}
- onFilterCountryChanged(country: CountryLookupDto | null): void {
- this.selectedCountry.set(country);
- this.filterForm.controls.countryId.setValue(country ? country.id : '');
- this.filterForm.controls.stateId.setValue('');
- this.selectedFilterState.set(null);
- if (country) {
- this.filterForm.controls.stateId.enable();
- } else {
- this.filterForm.controls.stateId.disable();
- }
+ onCountrySearchChanged(term: string): void {
+ this.countrySearch$.next(term);
}
- onFilterStateChanged(state: StateLookupDto | null): void {
- this.selectedFilterState.set(state);
+ onStateSearchChanged(term: string): void {
+ this.stateSearch$.next(term);
}
onApplyFilter(event?: Event): void {
@@ -186,10 +209,9 @@ export class CityList implements OnInit {
}
onResetFilter(): void {
- this.filterForm.reset({ countryId: '', stateId: '' });
+ this.filterForm.reset({ countryId: [], stateId: [] });
this.filterForm.controls.stateId.disable();
- this.selectedCountry.set(null);
- this.selectedFilterState.set(null);
+ this.stateOptions.set([]);
this.tableStore.reset();
}
diff --git a/src/app/features/global-masters/countries/data-access/country.service.ts b/src/app/features/global-masters/countries/data-access/country.service.ts
index 0e9f8173..f5d551b8 100644
--- a/src/app/features/global-masters/countries/data-access/country.service.ts
+++ b/src/app/features/global-masters/countries/data-access/country.service.ts
@@ -21,8 +21,11 @@ import { COUNTRY_ENDPOINTS } from './country.endpoints';
export class CountryService {
private readonly http = inject(HttpClient);
- getCountryDataTable(query: DataTableQuery, regionId: string | null = null): Observable> {
- const params = regionId ? new HttpParams().set('regionId', regionId) : undefined;
+ getCountryDataTable(query: DataTableQuery, regionIds: readonly string[] = []): Observable> {
+ let params = new HttpParams();
+ for (const regionId of regionIds) {
+ params = params.append('regionIds', regionId);
+ }
return this.http.post>(COUNTRY_ENDPOINTS.dataTable, query, { params });
}
diff --git a/src/app/features/global-masters/countries/pages/country-list/country-list.html b/src/app/features/global-masters/countries/pages/country-list/country-list.html
index 46f0f2dd..fc7003be 100644
--- a/src/app/features/global-masters/countries/pages/country-list/country-list.html
+++ b/src/app/features/global-masters/countries/pages/country-list/country-list.html
@@ -5,6 +5,8 @@
[totalRecords]="tableStore.totalRecords()"
[pageIndex]="tableStore.queryState.pageIndex()"
[pageSize]="tableStore.queryState.pageSize()"
+ [initialSortColumn]="tableStore.queryState.sortColumn()"
+ [initialSortDirection]="tableStore.queryState.sortDirection()"
tableTitle="Countries"
buttonTitle="Add"
[showSearch]="true"
@@ -25,11 +27,10 @@