Skip to content

Commit 7450e83

Browse files
committed
fix(arc-saas): review changes of onboarding list page
review changes of onboarding list page GH-76
1 parent 085ed06 commit 7450e83

File tree

8 files changed

+281
-96
lines changed

8 files changed

+281
-96
lines changed

projects/saas-ui/src/app/main/commands/get-tenant-details.command.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ import {
66
} from '@project-lib/core/api';
77

88
import {IAnyObject} from '@project-lib/core/i-any-object';
9-
import {tenantDetails} from '../../shared/models/tenantDetails.model';
9+
import {TenantDetails} from '../../shared/models/tenantDetails.model';
1010

11-
export class GetTenantDetailsCommand<T> extends GetAPICommand<tenantDetails[]> {
11+
export class GetTenantDetailsCommand<T> extends GetAPICommand<TenantDetails[]> {
1212
constructor(
1313
apiService: ApiService,
14-
adapter: IAdapter<tenantDetails[]>,
14+
adapter: IAdapter<TenantDetails[]>,
1515
appConfig: IAnyObject,
1616
) {
1717
super(

projects/saas-ui/src/app/main/components/onboarding-tenant-list/onboarding-tenant-list.component.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ <h2 class="heading">Onboarded Tenant List</h2>
2424
class="ag-theme-quartz"
2525
[columnDefs]="colDefs"
2626
[gridOptions]="gridOptions"
27+
(gridReady)="onGridReady($event)"
2728
>
2829
</ag-grid-angular>
2930
</div>
Lines changed: 104 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,131 @@
1-
import {ComponentFixture, TestBed} from '@angular/core/testing';
2-
3-
import {OnboardingTenantListComponent} from './onboarding-tenant-list.component';
1+
import {
2+
ComponentFixture,
3+
TestBed,
4+
fakeAsync,
5+
tick,
6+
} from '@angular/core/testing';
7+
import {Router} from '@angular/router';
8+
import {HttpClientTestingModule} from '@angular/common/http/testing';
49
import {ActivatedRoute} from '@angular/router';
5-
import {NbToastrService} from '@nebular/theme';
10+
import {Location} from '@angular/common';
611
import {of} from 'rxjs';
7-
import {TenantFacadeService} from '../../../shared/services';
8-
import {ApiService} from '@project-lib/core/api';
9-
import {HttpClientTestingModule} from '@angular/common/http/testing';
12+
import {TenantFacadeService} from '../../../shared/services/tenant-list-facade.service';
13+
import {OnboardingTenantListComponent} from './onboarding-tenant-list.component';
1014
import {APP_CONFIG} from '@project-lib/app-config';
15+
import {GridApi} from 'ag-grid-community';
16+
import {ThemeModule} from '@project-lib/theme/theme.module';
17+
import {NbThemeModule} from '@nebular/theme';
18+
import {AgGridModule} from 'ag-grid-angular';
1119

12-
const mockAppConfig = {};
1320
describe('OnboardingTenantListComponent', () => {
1421
let component: OnboardingTenantListComponent;
1522
let fixture: ComponentFixture<OnboardingTenantListComponent>;
23+
let tenantFacadeService: jasmine.SpyObj<TenantFacadeService>;
24+
let router: jasmine.SpyObj<Router>;
25+
let mockGridApi: jasmine.SpyObj<GridApi>;
26+
27+
const mockAppConfig = {
28+
baseApiUrl: 'https://api.example.com',
29+
};
30+
31+
const mockTenantData = [
32+
{
33+
id: 1,
34+
name: 'Company A',
35+
firstName: 'John',
36+
lastName: 'Doe',
37+
email: 'john.doe@companyA.com',
38+
address: {
39+
zip: '12345',
40+
country: 'USA',
41+
},
42+
subscription: {
43+
plan: {
44+
name: 'Premium',
45+
},
46+
status: 'Active',
47+
startDate: '2022-01-01T00:00:00Z',
48+
endDate: '2023-01-01T00:00:00Z',
49+
},
50+
},
51+
];
1652

1753
beforeEach(async () => {
54+
const tenantFacadeServiceSpy = jasmine.createSpyObj('TenantFacadeService', [
55+
'getTenantDetails',
56+
]);
57+
const routerSpy = jasmine.createSpyObj('Router', ['navigate']);
58+
const gridApiSpy = jasmine.createSpyObj('GridApi', [
59+
'setDatasource',
60+
'refreshCells',
61+
]);
62+
1863
await TestBed.configureTestingModule({
1964
declarations: [OnboardingTenantListComponent],
20-
imports: [HttpClientTestingModule],
65+
imports: [
66+
HttpClientTestingModule,
67+
ThemeModule,
68+
NbThemeModule.forRoot(),
69+
AgGridModule,
70+
],
2171
providers: [
22-
NbToastrService,
23-
ApiService,
2472
{provide: APP_CONFIG, useValue: mockAppConfig},
25-
2673
{
27-
provide: ActivatedRoute,
28-
useValue: {paramMap: of(new Map())}, // Mock ActivatedRoute
74+
provide: TenantFacadeService,
75+
useValue: {
76+
getTenantDetails: jasmine
77+
.createSpy('getTenantDetails')
78+
.and.returnValue(of(mockTenantData)),
79+
getTotalTenant: jasmine
80+
.createSpy('getTotalTenant')
81+
.and.returnValue(of()),
82+
},
2983
},
84+
{provide: Router, useValue: routerSpy},
85+
{provide: GridApi, useValue: gridApiSpy},
3086
{
31-
provide: TenantFacadeService,
32-
useValue: {paramMap: of(new Map())},
87+
provide: ActivatedRoute,
88+
useValue: {snapshot: {paramMap: {get: () => '1'}}},
3389
},
90+
Location,
3491
],
3592
}).compileComponents();
3693

3794
fixture = TestBed.createComponent(OnboardingTenantListComponent);
95+
router = TestBed.inject(Router) as jasmine.SpyObj<Router>;
96+
mockGridApi = TestBed.inject(GridApi) as jasmine.SpyObj<GridApi>;
3897
component = fixture.componentInstance;
39-
fixture.detectChanges();
98+
tenantFacadeService = TestBed.inject(
99+
TenantFacadeService,
100+
) as jasmine.SpyObj<TenantFacadeService>;
40101
});
41102

42-
it('should create', () => {
103+
it('should create the component', () => {
43104
expect(component).toBeTruthy();
44105
});
106+
107+
it('should initialize grid options correctly', () => {
108+
expect(component.gridOptions.pagination).toBe(true);
109+
expect(component.gridOptions.paginationPageSize).toBe(10);
110+
expect(component.gridOptions.rowHeight).toBe(60);
111+
});
112+
113+
it('should navigate to tenant registration page', () => {
114+
component.registerTenantPage();
115+
expect(router.navigate).toHaveBeenCalledWith(['main/create-tenant']);
116+
});
117+
118+
it('should handle error in `getPaginatedTenantDetails` method', () => {
119+
spyOn(console, 'error');
120+
(tenantFacadeService.getTenantDetails as jasmine.Spy).and.returnValue(
121+
of(undefined),
122+
);
123+
component.getPaginatedTenantDetails(1, 10).subscribe(data => {
124+
expect(console.error).toHaveBeenCalledWith(
125+
'Error processing response:',
126+
jasmine.anything(),
127+
);
128+
expect(data).toEqual([]);
129+
});
130+
});
45131
});

projects/saas-ui/src/app/main/components/onboarding-tenant-list/onboarding-tenant-list.component.ts

Lines changed: 93 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
11
import {Component, Inject} from '@angular/core';
22
import {ActivatedRoute, Router} from '@angular/router';
33
import {RouteComponentBaseDirective} from '@project-lib/core/route-component-base';
4-
import {ColDef, GridApi, GridOptions} from 'ag-grid-community';
4+
import {
5+
ColDef,
6+
GridApi,
7+
GridOptions,
8+
IDatasource,
9+
IGetRowsParams,
10+
} from 'ag-grid-community';
511
import {Location} from '@angular/common';
6-
import {Observable} from 'rxjs';
12+
import {Observable, combineLatest, map} from 'rxjs';
713
import {TenantFacadeService} from '../../../shared/services/tenant-list-facade.service';
814
import {Tenant} from '../../../shared/models';
915
import {AnyObject, BackendFilter} from '@project-lib/core/api';
1016
import {TenantStatus} from '../../../shared/enum/tenant-status.enum';
1117
import {APP_CONFIG} from '@project-lib/app-config';
1218
import {IAnyObject} from '@project-lib/core/i-any-object';
1319
import {EyeIconRendererComponent} from '../eye-icon-renderer/eye-icon-renderer.component';
14-
import {tenantDetails} from '../../../shared/models/tenantDetails.model';
20+
import {TenantDetails} from '../../../shared/models/tenantDetails.model';
1521
import {HttpClient} from '@angular/common/http';
1622

1723
@Component({
@@ -23,6 +29,7 @@ export class OnboardingTenantListComponent extends RouteComponentBaseDirective {
2329
gridApi: GridApi;
2430
params: AnyObject;
2531
gridOptions: GridOptions;
32+
limit = 10;
2633
defaultColDef: ColDef = {
2734
flex: 1,
2835
minWidth: 150,
@@ -44,15 +51,15 @@ export class OnboardingTenantListComponent extends RouteComponentBaseDirective {
4451
pagination: true,
4552

4653
alwaysShowHorizontalScroll: true,
47-
rowModelType: 'clientSide',
48-
paginationPageSize: 5,
49-
paginationPageSizeSelector: [5, 10, 20, 50, 100],
50-
cacheBlockSize: 5,
54+
rowModelType: 'infinite',
55+
paginationPageSize: this.limit,
56+
57+
paginationPageSizeSelector: [this.limit, 20, 50, 100],
58+
cacheBlockSize: this.limit,
5159
onGridReady: this.onGridReady.bind(this),
5260
rowHeight: 60,
5361
defaultColDef: {flex: 1},
5462
};
55-
this.getTenantDetails();
5663
}
5764

5865
colDefs: ColDef[] = [
@@ -120,56 +127,95 @@ export class OnboardingTenantListComponent extends RouteComponentBaseDirective {
120127
filter: 'agTextColumnFilter',
121128
floatingFilter: true,
122129
},
123-
{
124-
headerName: 'Actions',
125-
minWidth: 100,
126-
cellRenderer: EyeIconRendererComponent,
127-
},
130+
// {
131+
// headerName: 'Actions',
132+
// minWidth: 100,
133+
// cellRenderer: EyeIconRendererComponent,
134+
// },
128135
];
129136

130137
rowData: any[] = [];
131138
onGridReady(params: AnyObject) {
132139
this.gridApi = params.api;
140+
const dataSource: IDatasource = {
141+
getRows: (params: IGetRowsParams) => {
142+
const page = params.endRow / this.limit;
143+
const paginatedLeads = this.getPaginatedTenantDetails(page, this.limit);
144+
const totalLead = this.getTotal();
145+
combineLatest([paginatedLeads, totalLead]).subscribe(
146+
([data, count]) => {
147+
params.successCallback(data, count.count);
148+
},
149+
150+
err => {
151+
params.failCallback();
152+
},
153+
);
154+
},
155+
};
156+
params.api.setDatasource(dataSource);
133157
}
134158

135-
getTenantDetails() {
136-
this.tenantFacade.getTenantDetails().subscribe(resp => {
137-
this.rowData = resp.map(item => {
138-
if (item) {
139-
const fullTenantName = [
140-
item.contacts[0]?.firstName,
141-
' ',
142-
item.contacts[0]?.lastName,
143-
]
144-
.filter(ele => ele != null && ele.trim() != '')
145-
.join(' ');
159+
getPaginatedTenantDetails(
160+
page: number,
161+
limit: number,
162+
): Observable<AnyObject[]> {
163+
const filter: BackendFilter<TenantDetails> = {
164+
offset: limit * (page - 1),
165+
limit: limit,
166+
};
167+
return this.tenantFacade.getTenantDetails(filter).pipe(
168+
map(resp => {
169+
console.log(resp);
170+
try {
171+
const rows = resp.map(item => {
172+
if (item) {
173+
const fullTenantName = [
174+
item?.firstName || '',
175+
' ',
176+
item?.lastName || '',
177+
]
178+
.filter(ele => ele != null && ele.trim() != '')
179+
.join(' ');
146180

147-
const addressString = [item.address.zip, ' ', item.address.country]
148-
.filter(ele => ele != null && ele.trim() != '')
149-
.join(' ');
181+
const addressString = [
182+
item.address.zip,
183+
' ',
184+
item.address.country,
185+
]
186+
.filter(ele => ele != null && ele.trim() != '')
187+
.join(' ');
150188

151-
return {
152-
id: item.id,
153-
name: item.name,
154-
tenant_name: fullTenantName,
155-
email: item.contacts[0].email,
156-
address: addressString,
157-
planName: item.subscription?.plan.name,
158-
status: TenantStatus[item.subscription?.status],
159-
startDate: item.subscription?.startDate
160-
? new Date(item.subscription.startDate).toLocaleDateString()
161-
: 'N/A',
162-
endDate: item.subscription?.endDate
163-
? new Date(item.subscription.endDate).toLocaleDateString()
164-
: 'N/A',
165-
};
189+
return {
190+
id: item.id,
191+
name: item.name,
192+
tenant_name: fullTenantName,
193+
email: item.email,
194+
address: addressString,
195+
planName: item.subscription?.plan.name,
196+
status: TenantStatus[item.subscription?.status],
197+
startDate: item.subscription?.startDate
198+
? new Date(item.subscription.startDate).toLocaleDateString()
199+
: 'N/A',
200+
endDate: item.subscription?.endDate
201+
? new Date(item.subscription.endDate).toLocaleDateString()
202+
: 'N/A',
203+
};
204+
}
205+
});
206+
return rows;
207+
} catch (error) {
208+
console.error('Error processing response:', error);
209+
return [];
166210
}
167-
});
168-
if (this.gridApi) {
169-
this.gridApi.setRowData(this.rowData);
170-
}
171-
});
211+
}),
212+
);
172213
}
214+
215+
getTotal() {
216+
return this.tenantFacade.getTotalTenant();
217+
}
218+
173219
createCompanyLink(params: any) {
174220
const url = this.appConfig.baseApiUrl.replace(
175221
'//',

0 commit comments

Comments
 (0)