Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,6 @@ export class CustomerListComponent implements OnInit, OnDestroy, AfterViewInit {
},
];

this.dtOptions = {
scrollY: '325px',
paging: false,
responsive: true,
};

const customerIdToEdit$ = this.store.pipe(select(customerIdtoEdit));
this.customerIdToEditSubscription = customerIdToEdit$.subscribe((customerId: string) => {
this.currentCustomerIdToEdit = customerId;
Expand Down
71 changes: 39 additions & 32 deletions src/app/modules/shared/components/sidebar/sidebar.component.html
Original file line number Diff line number Diff line change
@@ -1,43 +1,50 @@
<div class="d-flex bg-grayTW-lightest dark:bg-grayTW-darker dark:text-whiteTW" id="wrapper">
<div class="border-right bg-whiteTW dark:bg-grayTW-dark" id="sidebar-wrapper">
<div class="sidebar-heading" style="text-align: center">
<img src="assets/img/ioet.png" width="90" height="auto" style="padding-top: 2rem; padding-bottom: 2rem;" alt="logo" />
</div>
<div id="wrapper" class="d-flex bg-grayTW-lightest dark:bg-grayTW-darker dark:text-whiteTW">
<div id="sidebar-wrapper" class="table-row border-r border-grayTW-lighter shadow-sm bg-whiteTW dark:bg-grayTW-dark dark:border-grayTW">
<div class="table-cell">
<div class="relative flex items-center border-b border-grayTW-lighter dark:border-grayTW sidebar-heading">
<img src="assets/img/ioet-logo-without-letters.png" alt="ioet-logo" width="50" height="auto" style="padding-top: 0.5rem; padding-left: 1rem;"/>
<h1 class="pl-2 pt-2" style="font-family:spinnaker,sans-serif">ioet</h1>
</div>
<app-user></app-user>
<div class="list-group list-group-flush bg-whiteTW dark:bg-grayTW-darker">
<a
*ngFor="let item of itemsSidebar"
[routerLink]="item.route"
routerLinkActive=""
class="list-group-item list-group-item-action bg-whiteTW dark:bg-grayTW-dark dark:text-whiteTW"
[ngClass]="{active: item.active}"
>
<i class="{{ item.icon }}"></i> {{ item.text }}
</a>
<a
*ngFor="let item of itemsSidebar"
[routerLink]="item.route"
routerLinkActive=""
class="font-poppins relative pl-4 py-3 bg-whiteTW dark:bg-grayTW-dark dark:text-whiteTW"
[ngClass]="{active: item.active}"
>
<em class="{{ item.icon }}"></em>
<h3 class="font-poppins text-base ml-4 pl-3 -mt-6 text-left" >{{ item.text }}</h3>
</a>
<div class="bg-whiteTW pt-2 pr-24 absolute bottom-0 -mb-10 dark:bg-grayTW-dark">
<a class="flex pl-4 mb-4 text-blackTW hover:text-grayTW dark:text-whiteTW" href="#">
<em class="fas fa-sign-out-alt"></em>
<span class="font-poppins pl-3 font-medium -mt-1 dark:text-whiteTW" (click)="logout()">Sign out</span>
</a>
<app-dark-mode class="relative left-40 -top-14"></app-dark-mode>
</div>
</div>
</div>
<div class="table-cell relative">
<span class="absolute cursor-pointer inset-y-0 right-0 min-h-screen w-0.5 hover:z-0 hover:bg-primaryTW-light" (click)="toggleSideBar()"></span>
<span class="group cursor-pointer absolute w-5 top-28 -right-3">
<img src="assets/icons/left-chevron.svg" alt="left-chevron" id="hide-sidebar" (click)="toggleSideBar()" width="20" height="20" class="bg-whiteTW rounded-full hover:bg-opacity-50 hover:bg-primaryTW-light sm:hidden md:block lg:block"/>
<div class="opacity-0 w-12 bg-blackTW text-whiteTW text-center text-xs rounded-lg py-2 absolute z-10 group-hover:opacity-100 bottom-full left-1/4 ml-4 -mb-6 px-1 pointer-events-none">Hide</div>
</span>
<span class="group cursor-pointer absolute top-20 -right-5">
<img src="assets/icons/right-chevron.svg" alt="right-chevron" id="show-sidebar" (click)="toggleSideBar()" width="20" height="20" class="bg-whiteTW rounded-full hover:bg-opacity-50 hover:bg-primaryTW-light sm:block md:hidden lg:hidden"/>
<div class="opacity-0 w-12 bg-blackTW text-whiteTW text-center text-xs rounded-lg py-2 absolute z-10 group-hover:opacity-100 bottom-full left-1/4 ml-4 -mb-6 px-1 pointer-events-none">Show</div>
</span>
</div>
</div>
<div id="page-content-wrapper">
<nav class="navbar navbar-expand-lg navbar-light border-bottom bg-whiteTW dark:bg-grayTW-dark">
<button class="btn bg-primaryTW hover:bg-primaryTW-dark text-whiteTW hover:text-whiteTW" id="menu-toggle">
Toggle Menu
</button>
<div class="dark-mode-toggle">
<app-dark-mode></app-dark-mode>
</div>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav ml-auto mt-2 mt-lg-0">
<app-user></app-user>
</ul>
</div>
</nav>
<div class="container-fluid px-0 full-height">
<div class="content_app h-100">
<div class="m-1 p-5 rounded-md bg-whiteTW dark:bg-grayTW-dark">
<router-outlet></router-outlet>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
34 changes: 24 additions & 10 deletions src/app/modules/shared/components/sidebar/sidebar.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ body {
#sidebar-wrapper {
min-height: 100vh;
margin-left: -15rem;
-webkit-transition: margin .25s ease-out;
-moz-transition: margin .25s ease-out;
-o-transition: margin .25s ease-out;
transition: margin .25s ease-out;
-webkit-transition: margin 0.25s ease-out;
-moz-transition: margin 0.25s ease-out;
-o-transition: margin 0.25s ease-out;
transition: margin 0.25s ease-out;
}

#sidebar-wrapper .sidebar-heading {
Expand All @@ -30,7 +30,7 @@ body {
margin-left: 0;
}

@media (min-width: 883px) {
@media (min-width: 769px) {
#sidebar-wrapper {
margin-left: 0;
}
Expand All @@ -41,6 +41,21 @@ body {
#wrapper.toggled #sidebar-wrapper {
margin-left: -15rem;
}
#hide-sidebar {
display: block;
}
#show-sidebar {
display: none;
}
}

@media (max-width: 768px) {
#hide-sidebar {
display: none;
}
#show-sidebar {
display: block;
}
}

.content_app {
Expand All @@ -51,7 +66,6 @@ body {
.active {
color: $primary;
font-weight: bold;
text-decoration: underline;
border-color: $primary;
background-color: transparent;
}
Expand All @@ -65,7 +79,7 @@ body {
}

.dark-mode-toggle {
position: absolute;
left: 50%;
transform: translateX(-50%);
}
position: absolute;
left: 50%;
transform: translateX(-50%);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ describe('SidebarComponent', () => {
isAdmin() {
return true;
},
logout(){
return true;
}
};

const userInfoServiceStub = {
Expand Down Expand Up @@ -50,19 +53,19 @@ describe('SidebarComponent', () => {
fixture.detectChanges();
});

it('should be created', () => {
it('component should be created', () => {
spyOn(azureAdB2CServiceStubInjected, 'isAdmin').and.returnValue(false);
expect(component).toBeTruthy();
});

it('admin users have six menu items', () => {
it('admin users should have six menu items', () => {
component.getSidebarItems().subscribe(() => {
const menuItems = component.itemsSidebar;
expect(menuItems.length).toBe(6);
});
});

it('non admin users have two menu items', () => {
it('non admin users should have two menu items', () => {
spyOn(userInfoServiceStub, 'isAdmin').and.returnValue(of(false));

component.getSidebarItems().subscribe(() => {
Expand All @@ -71,7 +74,7 @@ describe('SidebarComponent', () => {
});
});

it('when item is selected is should be set as active and the others as inactive', () => {
it('when item is selected should be set as active and the others as inactive', () => {
const route = 'time-clock';
router.navigate([route]);

Expand All @@ -83,4 +86,16 @@ describe('SidebarComponent', () => {
});
});

it('should toggle the sidebar', () => {
component.toggleSideBar();
fixture.detectChanges();
const sidebarElement = fixture.debugElement.nativeElement.querySelector('#wrapper');
expect(sidebarElement.classList.contains('toggled')).toBeTrue();
});

it('should use the Azure service on logout', () => {
spyOn(azureAdB2CServiceStubInjected, 'logout');
component.logout();
expect(azureAdB2CServiceStubInjected.logout).toHaveBeenCalled();
});
});
35 changes: 17 additions & 18 deletions src/app/modules/shared/components/sidebar/sidebar.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { NavigationStart, Router } from '@angular/router';
import { Observable, Subscription } from 'rxjs';
import { filter, map } from 'rxjs/operators';
import { UserInfoService } from 'src/app/modules/user/services/user-info.service';

import { AzureAdB2CService } from '../../../login/services/azure.ad.b2c.service';
@Component({
selector: 'app-sidebar',
templateUrl: './sidebar.component.html',
Expand All @@ -15,17 +15,13 @@ export class SidebarComponent implements OnInit, OnDestroy {
navStart;
sidebarItems$: Subscription;

constructor(
private router: Router,
private userInfoService: UserInfoService,
) {
constructor(private router: Router, private userInfoService: UserInfoService, private azureAdB2CService: AzureAdB2CService) {
this.navStart = this.router.events.pipe(
filter((evt) => evt instanceof NavigationStart)
) as Observable<NavigationStart>;
}

ngOnInit(): void {
this.toggleSideBar();
const currentRouting = this.router.routerState.snapshot.url;
this.sidebarItems$ = this.getSidebarItems().subscribe(() => this.highlightMenuOption(currentRouting));
this.navStart.subscribe((evt) => {
Expand All @@ -38,28 +34,27 @@ export class SidebarComponent implements OnInit, OnDestroy {
}

toggleSideBar() {
$('#menu-toggle').on('click', (e) => {
e.preventDefault();
$('#wrapper').toggleClass('toggled');
});
$('#wrapper').toggleClass('toggled');
$('#show-sidebar').toggle();
$('#hide-sidebar').toggle();
}

getSidebarItems(): Observable<void> {
return this.userInfoService.isAdmin().pipe(
map((isAdmin) => {
if (isAdmin) {
this.itemsSidebar = [
{ route: '/time-clock', icon: 'fas fa-clock', text: 'Time Clock', active: false },
{ route: '/time-entries', icon: 'fas fa-list-alt', text: 'Time Entries', active: false },
{ route: '/reports', icon: 'fas fa-chart-pie', text: 'Reports', active: false },
{ route: '/activities-management', icon: 'fas fa-file-alt', text: 'Activities', active: false },
{ route: '/customers-management', icon: 'fas fa-user', text: 'Customers', active: false },
{ route: '/users', icon: 'fas fa-user', text: 'Users', active: false },
{ route: '/time-clock', icon: 'far fa-clock', text: 'Time Clock', active: false },
{ route: '/time-entries', icon: 'far fa-file-alt', text: 'Time Entries', active: false },
{ route: '/reports', icon: 'fas fa-chart-bar', text: 'Reports', active: false },
{ route: '/activities-management', icon: 'fas fa-list-ol', text: 'Activities', active: false },
{ route: '/customers-management', icon: 'fas fa-users', text: 'Customers', active: false },
{ route: '/users', icon: 'fas fa-user-friends', text: 'Users', active: false },
];
} else {
this.itemsSidebar = [
{ route: '/time-clock', icon: 'fas fa-clock', text: 'Time Clock', active: false },
{ route: '/time-entries', icon: 'fas fa-list-alt', text: 'Time Entries', active: false },
{ route: '/time-clock', icon: 'far fa-clock', text: 'Time Clock', active: false },
{ route: '/time-entries', icon: 'far fa-file-alt', text: 'Time Entries', active: false },
];
}
})
Expand All @@ -70,4 +65,8 @@ export class SidebarComponent implements OnInit, OnDestroy {
this.itemsSidebar.map((item) => (item.active = false));
this.itemsSidebar.filter((item) => item.route === route).map((item) => (item.active = true));
}

logout() {
this.azureAdB2CService.logout();
}
}
17 changes: 7 additions & 10 deletions src/app/modules/shared/components/user/user.component.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
<li class="nav-item active dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
<i class="far fa-user-circle"></i>
{{name}}
</a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="#" (click)="logout()">Sign Out</a>
</div>
</li>
<div class=" py-8 flex items-center pl-3">
<img src="assets/icons/user.svg" alt="user-icon" width="40" height="40"/>
<span class="pl-1">
<h2 class="font-poppins text-base font-semibold pl-2 dark:text-whiteTW">{{userName}}</h2>
<p class="font-poppins mt-1 text-xs font-medium pl-2 text-grayTW dark:text-whiteTW">{{userEmail}}</p>
</span>
</div>
12 changes: 4 additions & 8 deletions src/app/modules/shared/components/user/user.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,28 +40,24 @@ describe('UserComponent', () => {
it('onInit checks if isLogin and gets the name and set tenantIn in the storage', () => {
spyOn(azureAdB2CService, 'isLogin').and.returnValue(true);
spyOn(azureAdB2CService, 'getName').and.returnValue('Name');
spyOn(azureAdB2CService, 'getUserEmail').and.returnValue('Email');
spyOn(azureAdB2CService, 'setTenantId');
component.ngOnInit();
expect(azureAdB2CService.isLogin).toHaveBeenCalled();
expect(azureAdB2CService.getName).toHaveBeenCalled();
expect(azureAdB2CService.getUserEmail).toHaveBeenCalled();
expect(azureAdB2CService.setTenantId).toHaveBeenCalled();
});

it('onInit does not get the name if isLogin false', () => {
spyOn(azureAdB2CService, 'isLogin').and.returnValue(false);
spyOn(azureAdB2CService, 'getName').and.returnValue('Name');
spyOn(azureAdB2CService, 'getUserEmail').and.returnValue('Email');
spyOn(azureAdB2CService, 'setTenantId');
component.ngOnInit();
expect(azureAdB2CService.isLogin).toHaveBeenCalled();
expect(azureAdB2CService.getName).toHaveBeenCalledTimes(0);
expect(azureAdB2CService.getUserEmail).toHaveBeenCalledTimes(0);
expect(azureAdB2CService.setTenantId).not.toHaveBeenCalled();
});

it('uses the Azure service on logout', () => {
spyOn(azureAdB2CService, 'logout');

component.logout();

expect(azureAdB2CService.logout).toHaveBeenCalled();
});
});
10 changes: 4 additions & 6 deletions src/app/modules/shared/components/user/user.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,16 @@ import { AzureAdB2CService } from '../../../login/services/azure.ad.b2c.service'
styleUrls: ['./user.component.scss'],
})
export class UserComponent implements OnInit {
name: string;
userName: string;
userEmail: string;

constructor(private azureAdB2CService: AzureAdB2CService) {}

ngOnInit(): void {
if (this.azureAdB2CService.isLogin()) {
this.name = this.azureAdB2CService.getName();
this.userName = this.azureAdB2CService.getName();
this.userEmail = this.azureAdB2CService.getUserEmail();
this.azureAdB2CService.setTenantId();
}
}

logout() {
this.azureAdB2CService.logout();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<h6 class="text-left"><strong>Summary</strong></h6>
<hr />
<hr class="mb-4" />
<div class ="container px-0">
<div class="row pb-4">
<div class="col-12 col-sm-3">
Expand Down
3 changes: 3 additions & 0 deletions src/assets/icons/activities.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/icons/clock.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/icons/customers.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading