Skip to content

Commit 032ebfe

Browse files
authored
fix: #569 create user options
* feat: #569 create users and users-list components * feat: #569 create a user store and consume the service of users * feat: #569 add test for user effects and selectors * fix: #569 fix PR comments * fix: #569 fix PR comments by Rene * fix: #569 remove column switch to admin * fix: #569 remove container from table
1 parent a474cf1 commit 032ebfe

25 files changed

+475
-3
lines changed

src/app/app-routing.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { ActivitiesManagementComponent } from './modules/activities-management/p
1010
import { HomeComponent } from './modules/home/home.component';
1111
import { LoginComponent } from './modules/login/login.component';
1212
import { CustomerComponent } from './modules/customer-management/pages/customer.component';
13+
import { UsersComponent } from './modules/users/pages/users.component';
1314

1415
const routes: Routes = [
1516
{
@@ -22,6 +23,7 @@ const routes: Routes = [
2223
{ path: 'time-entries', component: TimeEntriesComponent },
2324
{ path: 'activities-management', component: ActivitiesManagementComponent },
2425
{ path: 'customers-management', canActivate: [AdminGuard], component: CustomerComponent },
26+
{ path: 'users', canActivate: [AdminGuard], component: UsersComponent },
2527
{ path: '', pathMatch: 'full', redirectTo: 'time-clock' },
2628
],
2729
},

src/app/app.module.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ import { ProjectTypeListComponent } from './modules/customer-management/componen
5555
// tslint:disable-next-line: max-line-length
5656
import { CreateProjectTypeComponent } from './modules/customer-management/components/projects-type/components/create-project-type/create-project-type.component';
5757
import { CustomerEffects } from './modules/customer-management/store/customer-management.effects';
58+
import { UserEffects } from './modules/users/store/user.effects';
5859
import { EntryEffects } from './modules/time-clock/store/entry.effects';
5960
import { InjectTokenInterceptor } from './modules/shared/interceptors/inject.token.interceptor';
6061
import { SubstractDatePipe } from './modules/shared/pipes/substract-date/substract-date.pipe';
@@ -68,6 +69,8 @@ import { TimeRangeFormComponent } from './modules/reports/components/time-range-
6869
import { TimeEntriesTableComponent } from './modules/reports/components/time-entries-table/time-entries-table.component';
6970
import { DialogComponent } from './modules/shared/components/dialog/dialog.component';
7071
import { LoadingBarComponent } from './modules/shared/components/loading-bar/loading-bar.component';
72+
import { UsersComponent } from './modules/users/pages/users.component';
73+
import { UsersListComponent } from './modules/users/components/users-list/users-list.component';
7174

7275
const maskConfig: Partial<IConfig> = {
7376
validation: false,
@@ -115,7 +118,9 @@ const maskConfig: Partial<IConfig> = {
115118
TimeRangeFormComponent,
116119
TimeEntriesTableComponent,
117120
DialogComponent,
118-
LoadingBarComponent
121+
LoadingBarComponent,
122+
UsersComponent,
123+
UsersListComponent
119124
],
120125
imports: [
121126
NgxMaskModule.forRoot(maskConfig),
@@ -144,6 +149,7 @@ const maskConfig: Partial<IConfig> = {
144149
TechnologyEffects,
145150
ProjectTypeEffects,
146151
EntryEffects,
152+
UserEffects,
147153
]),
148154
ToastrModule.forRoot()
149155
],

src/app/modules/shared/components/sidebar/sidebar.component.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ describe('SidebarComponent', () => {
5656
component.getSidebarItems();
5757
const menuItems = component.itemsSidebar;
5858

59-
expect(menuItems.length).toBe(5);
59+
expect(menuItems.length).toBe(6);
6060
});
6161

6262
it('non admin users have two menu items', () => {

src/app/modules/shared/components/sidebar/sidebar.component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export class SidebarComponent implements OnInit {
4545
{route: '/reports', icon: 'fas fa-chart-pie', text: 'Reports', active: false},
4646
{route: '/activities-management', icon: 'fas fa-file-alt', text: 'Activities', active: false},
4747
{route: '/customers-management', icon: 'fas fa-user', text: 'Customers', active: false},
48+
{route: '/users', icon: 'fas fa-user', text: 'Users', active: false},
4849
];
4950
} else {
5051
this.itemsSidebar = [

src/app/modules/users/components/users-list/users-list.component.css

Whitespace-only changes.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<table *ngIf="users" class="table table-sm table-bordered table-striped mb-0" datatable [dtTrigger]="dtTrigger">
2+
<thead class="thead-blue">
3+
<tr class="d-flex">
4+
<th class="col-6">User Email</th>
5+
<th class="col-6">Names</th>
6+
</tr>
7+
</thead>
8+
<app-loading-bar *ngIf="isLoading$ | async"></app-loading-bar>
9+
<tbody *ngIf="(isLoading$ | async) === false">
10+
<tr class="d-flex" *ngFor="let user of users">
11+
<td class="col-sm-6">{{ user.email }}</td>
12+
<td class="col-sm-6">{{ user.name }}</td>
13+
</tr>
14+
</tbody>
15+
</table>
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2+
import { MockStore, provideMockStore } from '@ngrx/store/testing';
3+
4+
import { NgxPaginationModule } from 'ngx-pagination';
5+
import { UsersListComponent } from './users-list.component';
6+
import { UserActionTypes, UserState, LoadUsers } from '../../store';
7+
import { ActionsSubject } from '@ngrx/store';
8+
import { DataTablesModule } from 'angular-datatables';
9+
10+
describe('UsersListComponent', () => {
11+
let component: UsersListComponent;
12+
let fixture: ComponentFixture<UsersListComponent>;
13+
let store: MockStore<UserState>;
14+
const actionSub: ActionsSubject = new ActionsSubject();
15+
16+
const state: UserState = {
17+
data: [{ name: 'name', email: 'email', role: 'role', id: 'id', tenant_id: 'tenant id', deleted: 'delete' }],
18+
isLoading: false,
19+
message: '',
20+
};
21+
22+
beforeEach(async(() => {
23+
TestBed.configureTestingModule({
24+
imports: [NgxPaginationModule, DataTablesModule],
25+
declarations: [UsersListComponent],
26+
providers: [provideMockStore({ initialState: state }), { provide: ActionsSubject, useValue: actionSub }],
27+
}).compileComponents();
28+
}));
29+
30+
beforeEach(() => {
31+
fixture = TestBed.createComponent(UsersListComponent);
32+
component = fixture.componentInstance;
33+
store = TestBed.inject(MockStore);
34+
store.setState(state);
35+
fixture.detectChanges();
36+
});
37+
38+
it('should create', () => {
39+
expect(component).toBeTruthy();
40+
});
41+
42+
it('when the component is initialized the load User action is triggered', () => {
43+
spyOn(store, 'dispatch');
44+
45+
component.ngOnInit();
46+
47+
expect(store.dispatch).toHaveBeenCalledWith(new LoadUsers());
48+
});
49+
50+
it('on success load users, the user list should be populated', () => {
51+
const actionSubject = TestBed.inject(ActionsSubject) as ActionsSubject;
52+
const action = {
53+
type: UserActionTypes.LOAD_USERS_SUCCESS,
54+
payload: state.data,
55+
};
56+
57+
actionSubject.next(action);
58+
59+
expect(component.users).toEqual(state.data);
60+
});
61+
62+
it('on success load users, the datatable should be reloaded', async () => {
63+
const actionSubject = TestBed.inject(ActionsSubject);
64+
const action = {
65+
type: UserActionTypes.LOAD_USERS_SUCCESS,
66+
payload: state.data,
67+
};
68+
spyOn(component.dtElement.dtInstance, 'then');
69+
70+
actionSubject.next(action);
71+
72+
expect(component.dtElement.dtInstance.then).toHaveBeenCalled();
73+
});
74+
75+
afterEach(() => {
76+
component.dtTrigger.unsubscribe();
77+
component.loadUsersSubscription.unsubscribe();
78+
fixture.destroy();
79+
});
80+
});
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { AfterViewInit, Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
2+
import { ActionsSubject, select, Store } from '@ngrx/store';
3+
import { DataTableDirective } from 'angular-datatables';
4+
import { Observable, Subject, Subscription } from 'rxjs';
5+
import { delay, filter } from 'rxjs/operators';
6+
import { User } from '../../models/users';
7+
import { LoadUsers, UserActionTypes } from '../../store/user.actions';
8+
import { getIsLoading } from '../../store/user.selectors';
9+
@Component({
10+
selector: 'app-users-list',
11+
templateUrl: './users-list.component.html',
12+
styleUrls: ['./users-list.component.css'],
13+
})
14+
export class UsersListComponent implements OnInit, OnDestroy, AfterViewInit {
15+
users: User[] = [];
16+
isLoading$: Observable<boolean>;
17+
loadUsersSubscription: Subscription;
18+
dtTrigger: Subject<any> = new Subject();
19+
@ViewChild(DataTableDirective, { static: false })
20+
dtElement: DataTableDirective;
21+
22+
constructor(private store: Store<User>, private actionsSubject$: ActionsSubject) {
23+
this.isLoading$ = store.pipe(delay(0), select(getIsLoading));
24+
}
25+
26+
ngOnInit(): void {
27+
this.loadUsersSubscription = this.actionsSubject$
28+
.pipe(filter((action: any) => action.type === UserActionTypes.LOAD_USERS_SUCCESS))
29+
.subscribe((action) => {
30+
this.users = action.payload;
31+
this.rerenderDataTable();
32+
});
33+
this.store.dispatch(new LoadUsers());
34+
}
35+
36+
ngAfterViewInit(): void {
37+
this.rerenderDataTable();
38+
}
39+
40+
ngOnDestroy() {
41+
this.loadUsersSubscription.unsubscribe();
42+
this.dtTrigger.unsubscribe();
43+
}
44+
45+
private rerenderDataTable(): void {
46+
if (this.dtElement && this.dtElement.dtInstance) {
47+
this.dtElement.dtInstance.then((dtInstances: DataTables.Api) => {
48+
dtInstances.destroy();
49+
this.dtTrigger.next();
50+
});
51+
} else {
52+
this.dtTrigger.next();
53+
}
54+
}
55+
}

src/app/modules/users/models/users.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export interface User {
2+
name: string;
3+
email: string;
4+
role?: string;
5+
id: string;
6+
tenant_id?: string;
7+
deleted?: string;
8+
}

src/app/modules/users/pages/users.component.css

Whitespace-only changes.

0 commit comments

Comments
 (0)