Skip to content

Commit 7d8dc9e

Browse files
authored
Merge pull request #77 from sourcefuse/GH-76
fix(arc-saas): detailed list of tenant onboarding
2 parents 01a6716 + 7450e83 commit 7d8dc9e

18 files changed

+755
-84
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import {
2+
ApiService,
3+
GetAPICommand,
4+
GetListAPICommand,
5+
IAdapter,
6+
} from '@project-lib/core/api';
7+
8+
import {IAnyObject} from '@project-lib/core/i-any-object';
9+
import {TenantDetails} from '../../shared/models/tenantDetails.model';
10+
11+
export class GetTenantDetailsCommand<T> extends GetAPICommand<TenantDetails[]> {
12+
constructor(
13+
apiService: ApiService,
14+
adapter: IAdapter<TenantDetails[]>,
15+
appConfig: IAnyObject,
16+
) {
17+
super(
18+
apiService,
19+
adapter,
20+
`${appConfig.baseApiUrl}${appConfig.tenantMgmtFacadeUrl}/tenants`,
21+
);
22+
}
23+
}

projects/saas-ui/src/app/main/commands/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ export * from './add-features-for-plan.command';
1919
export * from './edit-features-by-planid.command';
2020
export * from './get-feature-by-planid.command';
2121
export * from './get-features.command';
22+
export * from './get-tenant-details.command';
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<nb-icon
2+
class="eye-icon"
3+
icon="eye-outline"
4+
pack="eva"
5+
status="primary"
6+
size="large"
7+
(click)="onClick($event)"
8+
></nb-icon>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
:host {
2+
display: flex;
3+
justify-content: center;
4+
align-items: center;
5+
}
6+
7+
.eye-icon{
8+
color: #ff3d71;
9+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { EyeIconRendererComponent } from './eye-icon-renderer.component';
4+
5+
describe('EyeIconRendererComponent', () => {
6+
let component: EyeIconRendererComponent;
7+
let fixture: ComponentFixture<EyeIconRendererComponent>;
8+
9+
beforeEach(async () => {
10+
await TestBed.configureTestingModule({
11+
declarations: [ EyeIconRendererComponent ]
12+
})
13+
.compileComponents();
14+
15+
fixture = TestBed.createComponent(EyeIconRendererComponent);
16+
component = fixture.componentInstance;
17+
fixture.detectChanges();
18+
});
19+
20+
it('should create', () => {
21+
expect(component).toBeTruthy();
22+
});
23+
});
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import {Component} from '@angular/core';
2+
import {Router} from '@angular/router';
3+
import {ICellRendererAngularComp} from 'ag-grid-angular';
4+
5+
@Component({
6+
selector: 'app-eye-icon-renderer',
7+
templateUrl: './eye-icon-renderer.component.html',
8+
styleUrls: ['./eye-icon-renderer.component.scss'],
9+
})
10+
export class EyeIconRendererComponent implements ICellRendererAngularComp {
11+
private params: any;
12+
13+
constructor(private router: Router) {}
14+
15+
agInit(params: any): void {
16+
this.params = params;
17+
}
18+
19+
refresh(params: any): boolean {
20+
return false;
21+
}
22+
23+
onClick(event: Event): void {
24+
event.stopPropagation();
25+
const rowDataId = this.params.node.data.id;
26+
console.log(rowDataId);
27+
this.router.navigate([`/main/tenant-details/${rowDataId}`]);
28+
}
29+
}

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

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,18 @@ <h2 class="heading">Onboarded Tenant List</h2>
1616
</div>
1717
</div>
1818

19-
<!-- loader -->
20-
<!-- <div *ngIf="loading" class="loader-overlay">
21-
<div class="loader"></div>
22-
</div> -->
23-
2419
<!-- ag-grid -->
25-
<div class="grid">
26-
<ag-grid-angular
27-
style="width: 1350px; height: 650px"
28-
class="ag-theme-quartz"
29-
[columnDefs]="colDefs"
30-
[gridOptions]="gridOptions"
31-
>
32-
</ag-grid-angular>
20+
<div class="grid-wrapper">
21+
<div class="grid">
22+
<ag-grid-angular
23+
style="width: calc(100vw-200px); height: 650px"
24+
class="ag-theme-quartz"
25+
[columnDefs]="colDefs"
26+
[gridOptions]="gridOptions"
27+
(gridReady)="onGridReady($event)"
28+
>
29+
</ag-grid-angular>
30+
</div>
3331
</div>
3432
</nb-card-body>
3533
</nb-card>

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

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
@use '../../styles/grid-styles.scss' as *;
22

3-
.ag-theme-quartz {
3+
.ag-theme-alpine {
44
width: 100%;
5-
height: 400;
5+
height: 100%;
6+
overflow-x: auto;
67
}
78

8-
.header-wrapper{
9+
.header-wrapper {
910
display: flex;
1011
justify-content: space-between;
1112
align-items: center;
@@ -16,7 +17,6 @@
1617
color: #19a5ff;
1718
}
1819

19-
// for test
2020
.loader-overlay {
2121
position: fixed;
2222
top: 0;
@@ -41,11 +41,19 @@
4141
}
4242

4343
@-webkit-keyframes spin {
44-
0% { transform: rotate(0deg); }
45-
100% { transform: rotate(360deg); }
44+
0% {
45+
transform: rotate(0deg);
46+
}
47+
100% {
48+
transform: rotate(360deg);
49+
}
4650
}
4751

4852
@keyframes spin {
49-
0% { transform: rotate(0deg); }
50-
100% { transform: rotate(360deg); }
53+
0% {
54+
transform: rotate(0deg);
55+
}
56+
100% {
57+
transform: rotate(360deg);
58+
}
5159
}
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
});

0 commit comments

Comments
 (0)