Add project files.
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
export enum ApiRoutes {
|
||||
UserRegister = '/api/user/register',
|
||||
UserLogin = '/api/user/login',
|
||||
UserProfile = '/api/user/profile',
|
||||
UserAttach = '/api/user/attach',
|
||||
UserDeattach = '/api/user/deattach',
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
import { Page404Component } from './authentication/page404/page404.component';
|
||||
import { AuthGuard } from './core/guard/auth.guard';
|
||||
import { AuthLayoutComponent } from './layout/app-layout/auth-layout/auth-layout.component';
|
||||
import { MainLayoutComponent } from './layout/app-layout/main-layout/main-layout.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: MainLayoutComponent,
|
||||
canActivate: [AuthGuard],
|
||||
children: [
|
||||
{ path: '', redirectTo: '/authentication/signin', pathMatch: 'full' },
|
||||
{
|
||||
path: 'dashboard',
|
||||
loadChildren: () =>
|
||||
import('./dashboard/dashboard.module').then((m) => m.DashboardModule),
|
||||
},
|
||||
{
|
||||
path: 'extra-pages',
|
||||
loadChildren: () =>
|
||||
import('./extra-pages/extra-pages.module').then(
|
||||
(m) => m.ExtraPagesModule
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'multilevel',
|
||||
loadChildren: () =>
|
||||
import('./multilevel/multilevel.module').then(
|
||||
(m) => m.MultilevelModule
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'authentication',
|
||||
component: AuthLayoutComponent,
|
||||
loadChildren: () =>
|
||||
import('./authentication/authentication.module').then(
|
||||
(m) => m.AuthenticationModule
|
||||
),
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
component: MainLayoutComponent,
|
||||
canActivate: [AuthGuard],
|
||||
loadChildren: () =>
|
||||
import('./pages/pages.module').then(
|
||||
(m) => m.PagesModule
|
||||
),
|
||||
},
|
||||
{ path: '**', component: Page404Component },
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes, {})],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class AppRoutingModule {
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
<app-page-loader></app-page-loader>
|
||||
<router-outlet></router-outlet>
|
||||
@@ -0,0 +1,39 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
describe('AppComponent',
|
||||
() => {
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
RouterTestingModule
|
||||
],
|
||||
declarations: [
|
||||
AppComponent
|
||||
],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
it('should create the app',
|
||||
() => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it(`should have as title 'spire'`,
|
||||
() => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app.title).toEqual('spire');
|
||||
});
|
||||
|
||||
it('should render title',
|
||||
() => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.nativeElement as HTMLElement;
|
||||
expect(compiled.querySelector('.content span')?.textContent).toContain('spire app is running!');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Event, Router, NavigationStart, NavigationEnd } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss'],
|
||||
})
|
||||
export class AppComponent {
|
||||
currentUrl!: string;
|
||||
|
||||
constructor(public _router: Router) {
|
||||
this._router.events.subscribe((routerEvent: Event) => {
|
||||
if (routerEvent instanceof NavigationStart) {
|
||||
this.currentUrl = routerEvent.url.substring(
|
||||
routerEvent.url.lastIndexOf('/') + 1
|
||||
);
|
||||
}
|
||||
if (routerEvent instanceof NavigationEnd) {
|
||||
/* empty */
|
||||
}
|
||||
window.scrollTo(0, 0);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// angular
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { LocationStrategy, HashLocationStrategy } from '@angular/common';
|
||||
import { HttpClientModule, HTTP_INTERCEPTORS, HttpClient } from '@angular/common/http';
|
||||
|
||||
// libs
|
||||
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
|
||||
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
|
||||
import { LoadingBarRouterModule } from '@ngx-loading-bar/router';
|
||||
import { LoadingBarHttpClientModule } from '@ngx-loading-bar/http-client';
|
||||
import { NgScrollbarModule } from 'ngx-scrollbar';
|
||||
|
||||
// app
|
||||
import { CoreModule } from './core/core.module';
|
||||
import { SharedModule } from './shared/shared.module';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { HeaderComponent } from './layout/header/header.component';
|
||||
import { PageLoaderComponent } from './layout/page-loader/page-loader.component';
|
||||
import { SidebarComponent } from './layout/sidebar/sidebar.component';
|
||||
import { RightSidebarComponent } from './layout/right-sidebar/right-sidebar.component';
|
||||
import { AuthLayoutComponent } from './layout/app-layout/auth-layout/auth-layout.component';
|
||||
import { MainLayoutComponent } from './layout/app-layout/main-layout/main-layout.component';
|
||||
import { ErrorInterceptor } from './core/interceptor/error.interceptor';
|
||||
import { JwtInterceptor } from './core/interceptor/jwt.interceptor';
|
||||
|
||||
export function createTranslateLoader(http: HttpClient) {
|
||||
return new TranslateHttpLoader(http, 'assets/i18n/', '.json');
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
HeaderComponent,
|
||||
PageLoaderComponent,
|
||||
SidebarComponent,
|
||||
RightSidebarComponent,
|
||||
AuthLayoutComponent,
|
||||
MainLayoutComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
BrowserAnimationsModule,
|
||||
AppRoutingModule,
|
||||
HttpClientModule,
|
||||
LoadingBarHttpClientModule,
|
||||
LoadingBarRouterModule,
|
||||
NgScrollbarModule,
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useFactory: createTranslateLoader,
|
||||
deps: [HttpClient],
|
||||
},
|
||||
}),
|
||||
// core & shared
|
||||
CoreModule,
|
||||
SharedModule,
|
||||
],
|
||||
providers: [
|
||||
{ provide: LocationStrategy, useClass: HashLocationStrategy },
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true },
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
export class AppModule {
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
import { SigninComponent } from './signin/signin.component';
|
||||
import { SignupComponent } from './signup/signup.component';
|
||||
import { ForgotPasswordComponent } from './forgot-password/forgot-password.component';
|
||||
import { LockedComponent } from './locked/locked.component';
|
||||
import { Page404Component } from './page404/page404.component';
|
||||
import { Page500Component } from './page500/page500.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
redirectTo: 'signin',
|
||||
pathMatch: 'full',
|
||||
},
|
||||
{
|
||||
path: 'signin',
|
||||
component: SigninComponent,
|
||||
},
|
||||
{
|
||||
path: 'signup',
|
||||
component: SignupComponent,
|
||||
},
|
||||
{
|
||||
path: 'forgot-password',
|
||||
component: ForgotPasswordComponent,
|
||||
},
|
||||
{
|
||||
path: 'locked',
|
||||
component: LockedComponent,
|
||||
},
|
||||
{
|
||||
path: 'page404',
|
||||
component: Page404Component,
|
||||
},
|
||||
{
|
||||
path: 'page500',
|
||||
component: Page500Component,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class AuthenticationRoutingModule {
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// angular
|
||||
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
|
||||
// libs
|
||||
import { GoogleSigninButtonModule } from '@abacritt/angularx-social-login';
|
||||
|
||||
// app
|
||||
import { AuthenticationRoutingModule } from './authentication-routing.module';
|
||||
import { Page500Component } from './page500/page500.component';
|
||||
import { Page404Component } from './page404/page404.component';
|
||||
import { SigninComponent } from './signin/signin.component';
|
||||
import { SignupComponent } from './signup/signup.component';
|
||||
import { LockedComponent } from './locked/locked.component';
|
||||
import { ForgotPasswordComponent } from './forgot-password/forgot-password.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
AuthenticationRoutingModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
MatIconModule,
|
||||
MatButtonModule,
|
||||
GoogleSigninButtonModule,
|
||||
],
|
||||
declarations: [
|
||||
Page500Component,
|
||||
Page404Component,
|
||||
SigninComponent,
|
||||
SignupComponent,
|
||||
LockedComponent,
|
||||
ForgotPasswordComponent,
|
||||
],
|
||||
providers: [
|
||||
],
|
||||
schemas: [
|
||||
CUSTOM_ELEMENTS_SCHEMA
|
||||
]
|
||||
})
|
||||
export class AuthenticationModule {
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<div class="auth-container">
|
||||
<div class="row auth-main">
|
||||
<div class="col-sm-6 px-0 d-none d-sm-block">
|
||||
<div class="left-img" style="background-image: url(assets/images/pages/bg-03.png);">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 auth-form-section">
|
||||
<div class="form-section">
|
||||
<div class="auth-wrapper">
|
||||
<h2 class="welcome-msg"> Reset Password </h2>
|
||||
<p class="auth-signup-text text-muted">Let Us Help You</p>
|
||||
<form class="validate-form" [formGroup]="authForm" (ngSubmit)="onSubmit()">
|
||||
<div class="row">
|
||||
<div class="col-xl-12 col-lg-12 col-md-12 col-sm-12 mb-2">
|
||||
<span class="error-subheader2 p-t-20 p-b-15">
|
||||
Enter your registered email address.
|
||||
</span>
|
||||
<mat-form-field class="example-full-width" appearance="outline">
|
||||
<mat-label>Email</mat-label>
|
||||
<input matInput formControlName="email" required>
|
||||
<mat-icon class="material-icons-two-tone color-icon p-3" matSuffix>mail</mat-icon>
|
||||
<mat-error *ngIf="authForm.get('email')?.hasError('required') || authForm.get('email')?.touched">
|
||||
Please enter a valid email address
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container-auth-form-btn mt-5">
|
||||
<button mat-flat-button color="primary" class="auth-form-btn" [disabled]="!authForm.valid " type="submit">
|
||||
Reset My Password
|
||||
</button>
|
||||
</div>
|
||||
<div class="w-full p-t-25 text-center">
|
||||
<div>
|
||||
<a routerLink="/authentication/signin" class="txt1">
|
||||
Login?
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { ForgotPasswordComponent } from './forgot-password.component';
|
||||
describe('ForgotPasswordComponent',
|
||||
() => {
|
||||
let component: ForgotPasswordComponent;
|
||||
let fixture: ComponentFixture<ForgotPasswordComponent>;
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ForgotPasswordComponent],
|
||||
}).compileComponents();
|
||||
})
|
||||
);
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ForgotPasswordComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
it('should create',
|
||||
() => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,50 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router, ActivatedRoute } from '@angular/router';
|
||||
import {
|
||||
UntypedFormBuilder,
|
||||
UntypedFormGroup,
|
||||
Validators,
|
||||
} from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'app-forgot-password',
|
||||
templateUrl: './forgot-password.component.html',
|
||||
styleUrls: ['./forgot-password.component.scss'],
|
||||
})
|
||||
export class ForgotPasswordComponent implements OnInit {
|
||||
authForm!: UntypedFormGroup;
|
||||
submitted = false;
|
||||
returnUrl!: string;
|
||||
|
||||
constructor(
|
||||
private formBuilder: UntypedFormBuilder,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.authForm = this.formBuilder.group({
|
||||
email: [
|
||||
'',
|
||||
[Validators.required, Validators.email, Validators.minLength(5)],
|
||||
],
|
||||
});
|
||||
// get return url from route parameters or default to '/'
|
||||
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
|
||||
}
|
||||
|
||||
get f() {
|
||||
return this.authForm.controls;
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
this.submitted = true;
|
||||
// stop here if form is invalid
|
||||
if (this.authForm.invalid) {
|
||||
return;
|
||||
} else {
|
||||
this.router.navigate(['/dashboard/main']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<div class="auth-container">
|
||||
<div class="row auth-main">
|
||||
<div class="col-sm-6 px-0 d-none d-sm-block">
|
||||
<div class="left-img" style="background-image: url(assets/images/pages/bg-01.png);">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 auth-form-section">
|
||||
<div class="form-section">
|
||||
<div class="auth-wrapper">
|
||||
<form class="validate-form" [formGroup]="authForm" (ngSubmit)="onSubmit()">
|
||||
<div class="auth-locked">
|
||||
<div class="image">
|
||||
<img src={{userImg}} alt="User">
|
||||
</div>
|
||||
</div>
|
||||
<span class="auth-locked-title p-b-34 p-t-27">
|
||||
{{userFullName}}
|
||||
</span>
|
||||
<div class="text-center">
|
||||
<p class="txt1 p-b-20">
|
||||
Locked
|
||||
</p>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xl-12 col-lg-12 col-md-12 col-sm-12 mb-2">
|
||||
<span class="error-subheader2 p-t-20 p-b-15">
|
||||
Enter your password here.
|
||||
</span>
|
||||
<mat-form-field class="example-full-width" appearance="outline">
|
||||
<mat-label>Password</mat-label>
|
||||
<input matInput formControlName="password" [type]="hide ? 'password' : 'text'" required>
|
||||
<mat-icon matSuffix (click)="hide = !hide">
|
||||
{{hide ? 'visibility_off' : 'visibility'}}
|
||||
</mat-icon>
|
||||
<mat-error *ngIf="authForm.get('password')?.hasError('required')">
|
||||
Password is required
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container-auth-form-btn mt-5">
|
||||
<button mat-flat-button color="primary" class="auth-form-btn" [disabled]="!authForm.valid " type="submit">
|
||||
Reset My Password
|
||||
</button>
|
||||
</div>
|
||||
<div class="w-full p-t-15 p-b-15 text-center">
|
||||
<div>
|
||||
<a routerLink="/authentication/signin" class="txt1">
|
||||
Need Help?
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { LockedComponent } from './locked.component';
|
||||
describe('LockedComponent',
|
||||
() => {
|
||||
let component: LockedComponent;
|
||||
let fixture: ComponentFixture<LockedComponent>;
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [LockedComponent],
|
||||
}).compileComponents();
|
||||
})
|
||||
);
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LockedComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
it('should create',
|
||||
() => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||
import { AuthService } from 'src/app/core/service/auth.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-locked',
|
||||
templateUrl: './locked.component.html',
|
||||
styleUrls: ['./locked.component.scss'],
|
||||
})
|
||||
export class LockedComponent implements OnInit {
|
||||
authForm!: UntypedFormGroup;
|
||||
submitted = false;
|
||||
userImg!: string;
|
||||
userFullName!: string;
|
||||
hide = true;
|
||||
|
||||
constructor(
|
||||
private formBuilder: UntypedFormBuilder,
|
||||
private router: Router,
|
||||
private authService: AuthService
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.authForm = this.formBuilder.group({
|
||||
password: ['', Validators.required],
|
||||
});
|
||||
|
||||
this.userImg = this.authService.currentUserValue.img || 'assets/images/user/admin.jpg';
|
||||
|
||||
this.userFullName =
|
||||
this.authService.currentUserValue.firstName +
|
||||
' ' +
|
||||
this.authService.currentUserValue.lastName;
|
||||
}
|
||||
|
||||
get f() {
|
||||
return this.authForm.controls;
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
this.submitted = true;
|
||||
// stop here if form is invalid
|
||||
if (this.authForm.invalid) {
|
||||
return;
|
||||
} else {
|
||||
this.router.navigate(['/dashboard/dashboard1']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<div class="auth-container">
|
||||
<div class="row auth-main">
|
||||
<div class="col-sm-6 px-0 d-none d-sm-block">
|
||||
<div class="left-img" style="background-image: url(assets/images/pages/bg-04.png);">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 auth-form-section">
|
||||
<div class="form-section">
|
||||
<div class="auth-wrapper">
|
||||
<form>
|
||||
<span class="error-header p-b-45">
|
||||
404
|
||||
</span>
|
||||
<span class="error-subheader p-b-5">
|
||||
Looks Like You're Lost
|
||||
</span>
|
||||
<span class="error-subheader2 p-b-5">
|
||||
The Page You Are Looking For Not Available!
|
||||
</span>
|
||||
<div class="container-auth-form-btn mt-5">
|
||||
<button mat-flat-button color="primary" class="auth-form-btn" type="submit" routerLink="/dashboard">
|
||||
Go To Home Page
|
||||
</button>
|
||||
</div>
|
||||
<div class="w-full p-t-15 p-b-15 text-center">
|
||||
<div>
|
||||
<a routerLink="/authentication/signin" class="txt1">
|
||||
Need Help?
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,24 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { Page404Component } from './page404.component';
|
||||
|
||||
describe('Page404Component',
|
||||
() => {
|
||||
let component: Page404Component;
|
||||
let fixture: ComponentFixture<Page404Component>;
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [Page404Component],
|
||||
}).compileComponents();
|
||||
})
|
||||
);
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(Page404Component);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
it('should create',
|
||||
() => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-page404',
|
||||
templateUrl: './page404.component.html',
|
||||
styleUrls: ['./page404.component.scss'],
|
||||
})
|
||||
export class Page404Component {
|
||||
constructor() {
|
||||
// constructor
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<div class="auth-container">
|
||||
<div class="row auth-main">
|
||||
<div class="col-sm-6 px-0 d-none d-sm-block">
|
||||
<div class="left-img" style="background-image: url(assets/images/pages/bg-05.png);">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 auth-form-section">
|
||||
<div class="form-section">
|
||||
<div class="auth-wrapper">
|
||||
<form>
|
||||
<span class="error-header p-b-45">
|
||||
500
|
||||
</span>
|
||||
<span class="error-subheader2 p-b-5">
|
||||
Oops, Something went wrong. Please try after some times.
|
||||
</span>
|
||||
<div class="container-auth-form-btn mt-5">
|
||||
<button mat-flat-button color="primary" class="auth-form-btn" type="submit">
|
||||
Go To Home Page
|
||||
</button>
|
||||
</div>
|
||||
<div class="w-full p-t-15 p-b-15 text-center">
|
||||
<div>
|
||||
<a routerLink="/authentication/signin" class="txt1">
|
||||
Need Help?
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,24 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { Page500Component } from './page500.component';
|
||||
|
||||
describe('Page500Component',
|
||||
() => {
|
||||
let component: Page500Component;
|
||||
let fixture: ComponentFixture<Page500Component>;
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [Page500Component],
|
||||
}).compileComponents();
|
||||
})
|
||||
);
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(Page500Component);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
it('should create',
|
||||
() => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-page500',
|
||||
templateUrl: './page500.component.html',
|
||||
styleUrls: ['./page500.component.scss'],
|
||||
})
|
||||
export class Page500Component {
|
||||
constructor() {
|
||||
// constructor
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
<div class="auth-container">
|
||||
<div class="row auth-main">
|
||||
<div class="col-sm-6 px-0 d-none d-sm-block">
|
||||
<div class="left-img" style="background-image: url(assets/images/pages/bg-01.png);">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 auth-form-section">
|
||||
<div class="form-section">
|
||||
<div class="auth-wrapper">
|
||||
<h2 class="welcome-msg">
|
||||
<div>Welcome to ase.com.ua</div>
|
||||
<div>angular/asp.net core template</div>
|
||||
</h2>
|
||||
<p class="auth-signup-text text-muted">
|
||||
Need an account?
|
||||
<a routerLink="/authentication/signup" class="sign-up-link">
|
||||
Sign Up
|
||||
</a>
|
||||
</p>
|
||||
<h2 class="login-title">Sign in</h2>
|
||||
<form class="validate-form" [formGroup]="authForm" (ngSubmit)="onSubmit()">
|
||||
<div class="row">
|
||||
<div class="col-xl-12 col-lg-12 col-md-12 col-sm-12 mb-2">
|
||||
<mat-form-field class="example-full-width" appearance="outline">
|
||||
<mat-label>Username</mat-label>
|
||||
<input matInput formControlName="username"/>
|
||||
<mat-icon class="material-icons-two-tone color-icon p-3" matSuffix>face</mat-icon>
|
||||
<mat-error *ngIf="authForm.get('username')?.hasError('required')">
|
||||
Username is required
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xl-12col-lg-12 col-md-12 col-sm-12 mb-2">
|
||||
<mat-form-field class="example-full-width" appearance="outline">
|
||||
<mat-label>Password</mat-label>
|
||||
<input matInput [type]="hide ? 'password' : 'text'" formControlName="password">
|
||||
<a href="#" onClick="return false;" matSuffix (click)="hide = !hide"
|
||||
[attr.aria-label]="'Hide password'" [attr.aria-pressed]="hide">
|
||||
<mat-icon class="material-icons-two-tone color-icon m-3" matSuffix>
|
||||
{{hide ? 'visibility_off' : 'visibility'}}
|
||||
</mat-icon>
|
||||
</a>
|
||||
<mat-error *ngIf="authForm.get('password')?.hasError('required')">
|
||||
Password is required
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center mb-5">
|
||||
<div class="form-check">
|
||||
<label class="form-check-label">
|
||||
<input class="form-check-input" type="checkbox" value=""> Remember me
|
||||
<span class="form-check-sign">
|
||||
<span class="check"></span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<a class="txt1" routerLink="/authentication/forgot-password">Forgot Password?</a>
|
||||
</div>
|
||||
|
||||
<div *ngIf="error" class="alert alert-danger mt-3 mb-0">{{error}}</div>
|
||||
|
||||
<div class="container-auth-form-btn">
|
||||
<div style="text-align: center">
|
||||
<button mat-raised-button color="primary" [class.auth-spinner]="loading" [disabled]="loading"
|
||||
class="auth-form-btn" [disabled]="!authForm.valid " type="submit">
|
||||
Login
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<h6 class="social-login-title">OR</h6>
|
||||
<ul class="list-unstyled social-icon mb-0 mt-3">
|
||||
<li class="list-inline-item">
|
||||
<a href="javascript:void(0)" class="rounded" (click)="loginAuth0()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 135 150">
|
||||
<title>auth0-glyph</title>
|
||||
<path d="M131.225 46.364L116.136 0H67.4l15.06 46.364h48.764zM67.4 0H18.664L3.602 46.364H52.34L67.399 0zM3.602 46.364c-8.982 27.639.856 57.918 24.368 75L43.032 75 3.602 46.364zm127.623 0L91.795 75l15.062 46.364c23.512-17.082 33.35-47.361 24.368-75zm-103.255 75L67.4 150l39.43-28.636L67.4 92.727l-39.43 28.637z" fill="#3c4858"/>
|
||||
</svg>
|
||||
</a>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<!--
|
||||
<a href="javascript:void(0)" class="rounded" (click)="loginGoogle()">
|
||||
<i class="fab fa-google"></i>
|
||||
</a>
|
||||
-->
|
||||
<asl-google-signin-button type="icon" size="medium" width="200" shape="rectangular" theme="filled_black" logo_alignment="center" locale=""></asl-google-signin-button>
|
||||
</li>
|
||||
<!--
|
||||
<li class="list-inline-item">
|
||||
<a href="javascript:void(0)" class="rounded flex-c-m">
|
||||
<i class="fab fa-facebook-f"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<a href="javascript:void(0)" class="rounded">
|
||||
<i class="fab fa-twitter"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<a href="javascript:void(0)" class="rounded">
|
||||
<i class="fab fa-linkedin-in"></i>
|
||||
</a>
|
||||
</li>
|
||||
-->
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,24 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { SigninComponent } from './signin.component';
|
||||
|
||||
describe('SigninComponent',
|
||||
() => {
|
||||
let component: SigninComponent;
|
||||
let fixture: ComponentFixture<SigninComponent>;
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [SigninComponent],
|
||||
}).compileComponents();
|
||||
})
|
||||
);
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SigninComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
it('should create',
|
||||
() => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,98 @@
|
||||
// angular
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router, ActivatedRoute } from '@angular/router';
|
||||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||
|
||||
// libs
|
||||
import { SocialAuthService } from '@abacritt/angularx-social-login';
|
||||
|
||||
// app
|
||||
import { AuthService } from 'src/app/core/service/auth.service';
|
||||
import { UnsubscribeOnDestroyAdapter } from 'src/app/shared/UnsubscribeOnDestroyAdapter';
|
||||
|
||||
@Component({
|
||||
selector: 'app-signin',
|
||||
templateUrl: './signin.component.html',
|
||||
styleUrls: ['./signin.component.scss'],
|
||||
})
|
||||
export class SigninComponent extends UnsubscribeOnDestroyAdapter
|
||||
implements OnInit {
|
||||
authForm!: UntypedFormGroup;
|
||||
submitted = false;
|
||||
loading = false;
|
||||
error?= '';
|
||||
hide = true;
|
||||
|
||||
constructor(
|
||||
private formBuilder: UntypedFormBuilder,
|
||||
private router: Router,
|
||||
private route: ActivatedRoute,
|
||||
private authService: AuthService,
|
||||
//private externalAuthService: SocialAuthService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.authForm = this.formBuilder.group({
|
||||
username: ['', Validators.required],
|
||||
password: ['', Validators.required],
|
||||
});
|
||||
|
||||
this.authService.isAuthenticated$.subscribe(isAuthenticated => {
|
||||
if (isAuthenticated) {
|
||||
this.router.navigate([this.getRedirect() || '/dashboard/dashboard1']);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
if (this.authForm.invalid) {
|
||||
this.error = 'Username and Password not valid!';
|
||||
return;
|
||||
}
|
||||
|
||||
this.submitted = true;
|
||||
this.loading = true;
|
||||
this.error = '';
|
||||
|
||||
var user = {
|
||||
username: this.authForm.controls['username'].value,
|
||||
password: this.authForm.controls['password'].value
|
||||
};
|
||||
|
||||
this.subs.sink = this.authService
|
||||
.login(user)
|
||||
.subscribe({
|
||||
next: (resp) => {
|
||||
if (!resp.success) {
|
||||
this.error = 'Invalid Login';
|
||||
if (!resp.success && resp.error?.code === 'invalid_grant') {
|
||||
this.error = 'Invalid username or password';
|
||||
}
|
||||
this.submitted = false;
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
error: (error) => {
|
||||
this.error = 'Invalid username or password';
|
||||
this.submitted = false;
|
||||
this.loading = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
loginAuth0() {
|
||||
this.authService.loginAuth0().subscribe(resp => {
|
||||
if (!resp.success) {
|
||||
this.error = 'Invalid login';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private getRedirect(): string | undefined {
|
||||
var redirectUrl = this.route.snapshot.queryParams['r'];
|
||||
redirectUrl = redirectUrl ? decodeURIComponent(redirectUrl) : undefined;
|
||||
return redirectUrl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
<div class="auth-container">
|
||||
<div class="row auth-main">
|
||||
<div class="col-sm-6 px-0 d-none d-sm-block">
|
||||
<div class="left-img" style="background-image: url(assets/images/pages/bg-02.png);">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 auth-form-section">
|
||||
<div class="form-section">
|
||||
<div class="auth-wrapper">
|
||||
<h2 class="welcome-msg"> Sign Up </h2>
|
||||
<p class="auth-signup-text text-muted">Enter details to create your account</p>
|
||||
<form class="validate-form" [formGroup]="authForm" (ngSubmit)="onSubmit()">
|
||||
<div class="row">
|
||||
<div class="col-xl-12col-lg-12 col-md-12 col-sm-12 mb-2">
|
||||
<mat-form-field class="example-full-width" appearance="outline">
|
||||
<mat-label>Email</mat-label>
|
||||
<input matInput formControlName="email" required autocomplete="off">
|
||||
<mat-icon class="material-icons-two-tone color-icon p-3" matSuffix>mail</mat-icon>
|
||||
<mat-error *ngIf="authForm.get('email')?.hasError('required') || authForm.get('email')?.touched">
|
||||
Please enter a valid email address
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xl-12col-lg-12 col-md-12 col-sm-12 mb-2">
|
||||
<mat-form-field class="example-full-width" appearance="outline">
|
||||
<mat-label>Password</mat-label>
|
||||
<input matInput formControlName="password" [type]="hide ? 'password' : 'text'" required autocomplete="off">
|
||||
<a href="#" onClick="return false;" matSuffix (click)="hide = !hide"
|
||||
[attr.aria-label]="'Hide password'" [attr.aria-pressed]="hide">
|
||||
<mat-icon class="material-icons-two-tone color-icon m-3" matSuffix>
|
||||
{{hide ? 'visibility_off' : 'visibility'}}
|
||||
</mat-icon>
|
||||
</a>
|
||||
<mat-error *ngIf="authForm.get('password')?.hasError('required')">
|
||||
Password is required
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xl-12col-lg-12 col-md-12 col-sm-12 mb-2">
|
||||
<mat-form-field class="example-full-width" appearance="outline">
|
||||
<mat-label>Confirm Password</mat-label>
|
||||
<input matInput formControlName="cpassword" [type]="chide ? 'password' : 'text'" required autocomplete="off">
|
||||
<a href="#" onClick="return false;" matSuffix (click)="chide = !chide"
|
||||
[attr.aria-label]="'Hide password'" [attr.aria-pressed]="chide">
|
||||
<mat-icon class="material-icons-two-tone color-icon m-3" matSuffix>
|
||||
{{hide ? 'visibility_off' : 'visibility'}}
|
||||
</mat-icon>
|
||||
</a>
|
||||
<mat-error *ngIf="authForm.get('cpassword')?.hasError('required')">
|
||||
Confirm Password is required
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-sb-m w-full p-b-20" *ngIf="generalError">
|
||||
<div *ngFor="let error of generalError.errors" class="alert alert-danger">
|
||||
{{error.description}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-sb-m w-full p-b-20">
|
||||
<div>
|
||||
<span>
|
||||
Already Registered?
|
||||
<a routerLink="/authentication/signin">
|
||||
Login
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container-auth-form-btn">
|
||||
<button mat-flat-button color="primary" class="auth-form-btn" [disabled]="!authForm.valid " type="submit">
|
||||
Register
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<h6 class="social-login-title">OR</h6>
|
||||
<ul class="list-unstyled social-icon mb-0 mt-3">
|
||||
<li class="list-inline-item">
|
||||
<a href="javascript:void(0)" class="rounded" (click)="loginAuth0()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 135 150">
|
||||
<title>auth0-glyph</title>
|
||||
<path d="M131.225 46.364L116.136 0H67.4l15.06 46.364h48.764zM67.4 0H18.664L3.602 46.364H52.34L67.399 0zM3.602 46.364c-8.982 27.639.856 57.918 24.368 75L43.032 75 3.602 46.364zm127.623 0L91.795 75l15.062 46.364c23.512-17.082 33.35-47.361 24.368-75zm-103.255 75L67.4 150l39.43-28.636L67.4 92.727l-39.43 28.637z" fill="#3c4858"/>
|
||||
</svg>
|
||||
</a>
|
||||
</li>
|
||||
<!--
|
||||
<li class="list-inline-item">
|
||||
<a href="javascript:void(0)" class="rounded">
|
||||
<i class="fab fa-google"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<a href="javascript:void(0)" class="rounded flex-c-m">
|
||||
<i class="fab fa-facebook-f"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<a href="javascript:void(0)" class="rounded">
|
||||
<i class="fab fa-twitter"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<a href="javascript:void(0)" class="rounded">
|
||||
<i class="fab fa-linkedin-in"></i>
|
||||
</a>
|
||||
</li>
|
||||
-->
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,24 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { SignupComponent } from './signup.component';
|
||||
|
||||
describe('SignupComponent',
|
||||
() => {
|
||||
let component: SignupComponent;
|
||||
let fixture: ComponentFixture<SignupComponent>;
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [SignupComponent],
|
||||
}).compileComponents();
|
||||
})
|
||||
);
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SignupComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
it('should create',
|
||||
() => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,72 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router, ActivatedRoute } from '@angular/router';
|
||||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||
import { GeneralErrorModel } from '../../core/models/general-error.model';
|
||||
import { AuthService } from '../../core/service/auth.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-signup',
|
||||
templateUrl: './signup.component.html',
|
||||
styleUrls: ['./signup.component.scss'],
|
||||
})
|
||||
export class SignupComponent implements OnInit {
|
||||
authForm!: UntypedFormGroup;
|
||||
submitted = false;
|
||||
returnUrl!: string;
|
||||
hide = true;
|
||||
chide = true;
|
||||
generalError?: GeneralErrorModel;
|
||||
|
||||
constructor(
|
||||
private formBuilder: UntypedFormBuilder,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private authService: AuthService
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.authForm = this.formBuilder.group({
|
||||
email: ['', [Validators.required, Validators.email, Validators.minLength(5)]],
|
||||
password: ['', Validators.required],
|
||||
cpassword: ['', Validators.required],
|
||||
});
|
||||
// get return url from route parameters or default to '/'
|
||||
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
|
||||
}
|
||||
|
||||
get f() {
|
||||
return this.authForm.controls;
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
this.submitted = true;
|
||||
// stop here if form is invalid
|
||||
if (this.authForm.invalid) {
|
||||
return;
|
||||
}
|
||||
//this.router.navigate(['/admin/dashboard/main']);
|
||||
this.authService.register({
|
||||
username: this.authForm.get('email')!.value!,
|
||||
password: this.authForm.get('password')!.value!,
|
||||
confirmPassword: this.authForm.get('cpassword')!.value!,
|
||||
}).subscribe(
|
||||
resp => {
|
||||
if (!resp.succeeded) {
|
||||
this.generalError = resp as GeneralErrorModel;
|
||||
} else {
|
||||
this.router.navigate(['authentication/signin']);
|
||||
}
|
||||
},
|
||||
err => {
|
||||
this.generalError = err.error as GeneralErrorModel;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
loginAuth0() {
|
||||
this.authService.loginAuth0().subscribe(x => {
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import { SocialAuthServiceConfig } from '@abacritt/angularx-social-login';
|
||||
import { GoogleLoginProvider } from '@abacritt/angularx-social-login';
|
||||
import { FacebookLoginProvider } from '@abacritt/angularx-social-login';
|
||||
import { MicrosoftLoginProvider } from '@abacritt/angularx-social-login';
|
||||
|
||||
import { environment } from '../environments/environment';
|
||||
|
||||
export class ExternalLoginConfig {
|
||||
|
||||
static readonly GOOGLE = 'google';
|
||||
static readonly AUTH0 = 'auth0';
|
||||
static readonly FACEBOOK = 'facebook';
|
||||
static readonly MICROSOFT = 'microsoft';
|
||||
|
||||
static getConfiguredProviders() {
|
||||
return [
|
||||
{
|
||||
provider: ExternalLoginConfig.GOOGLE,
|
||||
name: 'Google',
|
||||
},
|
||||
{
|
||||
provider: ExternalLoginConfig.AUTH0,
|
||||
name: 'Auth0',
|
||||
},
|
||||
/*{
|
||||
provider: ExternalLoginConfig.FACEBOOK,
|
||||
name: "FaceBook",
|
||||
},
|
||||
{
|
||||
provider: ExternalLoginConfig.MICROSOFT,
|
||||
name: "Microsoft",
|
||||
},*/
|
||||
];
|
||||
}
|
||||
|
||||
static getSocialConfig(): SocialAuthServiceConfig {
|
||||
var providers = [];
|
||||
|
||||
if (environment.externalLogins &&
|
||||
environment.externalLogins.google &&
|
||||
environment.externalLogins.google.clientId
|
||||
) {
|
||||
providers.push({
|
||||
id: GoogleLoginProvider.PROVIDER_ID,
|
||||
provider: new GoogleLoginProvider(
|
||||
environment.externalLogins.google.clientId,
|
||||
{ scopes: 'email', }
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
autoLogin: false,
|
||||
providers: providers,
|
||||
onError: (err) => {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static getAuth0Config() {
|
||||
return {
|
||||
clientId: environment.externalLogins.auth0.clientId,
|
||||
domain: environment.externalLogins.auth0.domain,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { AuthConfig } from 'angular-oauth2-oidc';
|
||||
import { OAuthModuleConfig } from 'angular-oauth2-oidc';
|
||||
|
||||
import { environment } from '../environments/environment';
|
||||
|
||||
export const AuthCodeFlowConfig: AuthConfig = {
|
||||
// Url of the Identity Provider
|
||||
issuer: environment.identityServer,
|
||||
requireHttps: false,
|
||||
strictDiscoveryDocumentValidation: false,
|
||||
|
||||
// URL of the SPA to redirect the user to after login
|
||||
redirectUri: window.location.origin + '/',
|
||||
silentRefreshRedirectUri: window.location.origin + '/silent-refresh.html',
|
||||
useSilentRefresh: true,
|
||||
silentRefreshTimeout: 5000, // For faster testing
|
||||
timeoutFactor: 0.25, // For faster testing
|
||||
sessionChecksEnabled: true,
|
||||
|
||||
// The SPA's id. The SPA is registerd with this id at the auth-server
|
||||
// clientId: 'server.code',
|
||||
clientId: 'angulartemplate_spa',
|
||||
|
||||
// Just needed if your auth server demands a secret. In general, this
|
||||
// is a sign that the auth server is not configured with SPAs in mind
|
||||
// and it might not enforce further best practices vital for security
|
||||
// such applications.
|
||||
// dummyClientSecret: 'secret',
|
||||
|
||||
responseType: 'code',
|
||||
|
||||
// set the scope for the permissions the client should request
|
||||
// The first four are defined by OIDC.
|
||||
// Important: Request offline_access to get a refresh token
|
||||
// The api scope is a usecase specific one
|
||||
scope: 'openid profile email api',
|
||||
|
||||
showDebugInformation: true,
|
||||
};
|
||||
|
||||
export const AuthModuleConfig: OAuthModuleConfig = {
|
||||
resourceServer: {
|
||||
allowedUrls: environment.allowedUrls,
|
||||
sendAccessToken: true,
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { InConfiguration } from '../core/models/config.interface';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ConfigService {
|
||||
configData!: InConfiguration;
|
||||
|
||||
constructor() {
|
||||
this.setConfigData();
|
||||
}
|
||||
|
||||
setConfigData() {
|
||||
this.configData = {
|
||||
layout: {
|
||||
rtl: false, // options: true & false
|
||||
variant: 'light', // options: light & dark
|
||||
theme_color: 'white', // options: white, black, purple, blue, cyan, green, orange
|
||||
logo_bg_color: 'white', // options: white, black, purple, blue, cyan, green, orange
|
||||
sidebar: {
|
||||
collapsed: false, // options: true & false
|
||||
backgroundColor: 'light', // options: light & dark
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// angular
|
||||
import { NgModule, Optional, SkipSelf, CUSTOM_ELEMENTS_SCHEMA, APP_INITIALIZER } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
// libs
|
||||
import { OAuthModule, OAuthModuleConfig, OAuthStorage } from 'angular-oauth2-oidc';
|
||||
import { AuthConfig } from 'angular-oauth2-oidc';
|
||||
import { AuthModule } from '@auth0/auth0-angular';
|
||||
import { SocialLoginModule } from '@abacritt/angularx-social-login';
|
||||
|
||||
// app
|
||||
import { AuthGuard } from './guard/auth.guard';
|
||||
import { RightSidebarService } from './service/rightsidebar.service';
|
||||
import { AuthService, authAppInitializerFactory } from './service/auth.service';
|
||||
import { DirectionService } from './service/direction.service';
|
||||
import { throwIfAlreadyLoaded } from './guard/module-import.guard';
|
||||
import { OidcHelperService } from './service/oidc-helper.service';
|
||||
import { AuthCodeFlowConfig, AuthModuleConfig } from '../config.oidc';
|
||||
import { SubjectExtensions } from './extensions/general.extensions';
|
||||
import { ExternalLoginConfig } from '../config.external-login';
|
||||
|
||||
export function storageFactory(): OAuthStorage {
|
||||
return localStorage;
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
imports: [
|
||||
CommonModule,
|
||||
SocialLoginModule,
|
||||
OAuthModule.forRoot(),
|
||||
AuthModule.forRoot(ExternalLoginConfig.getAuth0Config()),
|
||||
],
|
||||
providers: [
|
||||
{ provide: APP_INITIALIZER, useFactory: authAppInitializerFactory, deps: [AuthService], multi: true },
|
||||
{ provide: AuthConfig, useValue: AuthCodeFlowConfig },
|
||||
{ provide: OAuthModuleConfig, useValue: AuthModuleConfig },
|
||||
{ provide: OAuthStorage, useFactory: storageFactory },
|
||||
{ provide: 'SocialAuthServiceConfig', useValue: ExternalLoginConfig.getSocialConfig() },
|
||||
RightSidebarService,
|
||||
AuthGuard,
|
||||
AuthService,
|
||||
DirectionService,
|
||||
OidcHelperService,
|
||||
SubjectExtensions,
|
||||
],
|
||||
schemas: [
|
||||
CUSTOM_ELEMENTS_SCHEMA
|
||||
]
|
||||
})
|
||||
export class CoreModule {
|
||||
constructor(@Optional() @SkipSelf() parentModule: CoreModule) {
|
||||
throwIfAlreadyLoaded(parentModule, 'CoreModule');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// angular
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
// libs
|
||||
import { BehaviorSubject, Observable, Subject, of, throwError, from, combineLatest } from 'rxjs';
|
||||
|
||||
export { }
|
||||
|
||||
declare global {
|
||||
interface RouterExtensions {
|
||||
addDays(days: number): Date;
|
||||
}
|
||||
}
|
||||
|
||||
export class ExActivatedRoute extends ActivatedRoute {
|
||||
|
||||
getCurrentRoute(): string {
|
||||
var redirectUrl = this.snapshot.queryParams['r'];
|
||||
redirectUrl = redirectUrl ? decodeURIComponent(redirectUrl) : undefined;
|
||||
return redirectUrl;
|
||||
}
|
||||
}
|
||||
|
||||
interface Action<T> {
|
||||
(item: T): void;
|
||||
}
|
||||
|
||||
interface Func<T, TResult> {
|
||||
(item: T): TResult;
|
||||
}
|
||||
|
||||
export class SubjectExtensions {
|
||||
|
||||
static start<T>(start: Action<Subject<T>>): Subject<T> {
|
||||
var result = new Subject<T>();
|
||||
|
||||
start(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
export { }
|
||||
|
||||
declare global {
|
||||
interface RouterExtensions {
|
||||
addDays(days: number): Date;
|
||||
}
|
||||
}
|
||||
|
||||
export class ExActivatedRoute extends ActivatedRoute {
|
||||
|
||||
getCurrentRoute(): string {
|
||||
var redirectUrl = this.snapshot.queryParams['r'];
|
||||
redirectUrl = redirectUrl ? decodeURIComponent(redirectUrl) : undefined;
|
||||
return redirectUrl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
|
||||
|
||||
import { AuthService } from '../service/auth.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class AuthGuard implements CanActivate {
|
||||
constructor(
|
||||
private authService: AuthService,
|
||||
private router: Router
|
||||
) {
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
if (this.authService.isAuthenticated) {
|
||||
return true;
|
||||
}
|
||||
this.router.navigate(['/authentication/signin'], { queryParams: { r: encodeURIComponent(state.url) } });
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { CoreModule } from '../core.module';
|
||||
|
||||
export function throwIfAlreadyLoaded(
|
||||
parentModule: CoreModule,
|
||||
moduleName: string
|
||||
) {
|
||||
if (parentModule) {
|
||||
throw new Error(
|
||||
`${moduleName} has already been loaded. Import ${moduleName} modules in the AppModule only.`
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { AuthService } from '../service/auth.service';
|
||||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
HttpRequest,
|
||||
HttpHandler,
|
||||
HttpEvent,
|
||||
HttpInterceptor,
|
||||
} from '@angular/common/http';
|
||||
import { Observable, throwError } from 'rxjs';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
|
||||
@Injectable()
|
||||
export class ErrorInterceptor implements HttpInterceptor {
|
||||
constructor(private authenticationService: AuthService) {}
|
||||
|
||||
intercept(
|
||||
request: HttpRequest<any>,
|
||||
next: HttpHandler
|
||||
): Observable<HttpEvent<any>> {
|
||||
return next.handle(request).pipe(
|
||||
catchError((err) => {
|
||||
if (err.status === 401) {
|
||||
// auto logout if 401 response returned from api
|
||||
this.authenticationService.logout();
|
||||
location.reload();
|
||||
}
|
||||
|
||||
const error = err.error.message || err.statusText;
|
||||
//return throwError(error);
|
||||
return throwError(err.error || err);
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*import { Injectable } from '@angular/core';
|
||||
import {
|
||||
HttpRequest,
|
||||
HttpResponse,
|
||||
HttpHandler,
|
||||
HttpEvent,
|
||||
HttpInterceptor,
|
||||
HTTP_INTERCEPTORS,
|
||||
} from '@angular/common/http';
|
||||
import { Observable, of, throwError } from 'rxjs';
|
||||
import { mergeMap } from 'rxjs/operators';
|
||||
import { UserModel } from '../models/user.model';
|
||||
|
||||
const users: UserModel[] = [
|
||||
{
|
||||
id: 'fake-user',
|
||||
img: 'assets/images/user/admin.jpg',
|
||||
username: 'admin@software.com',
|
||||
//password: 'admin@123',
|
||||
firstName: 'Sarah',
|
||||
lastName: 'Smith',
|
||||
token: 'admin-token',
|
||||
},
|
||||
];
|
||||
|
||||
@Injectable()
|
||||
export class FakeBackendInterceptor implements HttpInterceptor {
|
||||
intercept(
|
||||
request: HttpRequest<any>,
|
||||
next: HttpHandler
|
||||
): Observable<HttpEvent<any>> {
|
||||
const { url, method, headers, body } = request;
|
||||
// wrap in delayed observable to simulate server api call
|
||||
return of(null).pipe(mergeMap(handleRoute));
|
||||
|
||||
function handleRoute() {
|
||||
switch (true) {
|
||||
case url.endsWith('/authenticate') && method === 'POST':
|
||||
return authenticate();
|
||||
default:
|
||||
// pass through any requests not handled above
|
||||
return next.handle(request);
|
||||
}
|
||||
}
|
||||
|
||||
// route functions
|
||||
|
||||
function authenticate() {
|
||||
const { username, password } = body;
|
||||
const user = users.find(
|
||||
(x) => x.username === username && x.password === password
|
||||
);
|
||||
if (!user) {
|
||||
return error('Username or password is incorrect');
|
||||
}
|
||||
return ok({
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
img: user.img,
|
||||
firstName: user.firstName,
|
||||
lastName: user.lastName,
|
||||
token: user.token,
|
||||
});
|
||||
}
|
||||
|
||||
// helper functions
|
||||
|
||||
function ok(body?: {
|
||||
id: string;
|
||||
username: string;
|
||||
img?: string;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
token?: string;
|
||||
}) {
|
||||
return of(new HttpResponse({ status: 200, body }));
|
||||
}
|
||||
|
||||
function error(message: string) {
|
||||
return throwError({ error: { message } });
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
function unauthorized() {
|
||||
return throwError({ status: 401, error: { message: 'Unauthorised' } });
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
function isLoggedIn() {
|
||||
return headers.get('Authorization') === 'Bearer fake-jwt-token';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const fakeBackendProvider = {
|
||||
// use fake backend in place of Http service for backend-less development
|
||||
provide: HTTP_INTERCEPTORS,
|
||||
useClass: FakeBackendInterceptor,
|
||||
multi: true,
|
||||
};
|
||||
*/
|
||||
@@ -0,0 +1,29 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
HttpRequest,
|
||||
HttpHandler,
|
||||
HttpEvent,
|
||||
HttpInterceptor,
|
||||
} from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
import { AuthService } from '../service/auth.service';
|
||||
|
||||
@Injectable()
|
||||
export class JwtInterceptor implements HttpInterceptor {
|
||||
constructor(private authenticationService: AuthService) {}
|
||||
|
||||
intercept(
|
||||
request: HttpRequest<any>,
|
||||
next: HttpHandler
|
||||
): Observable<HttpEvent<any>> {
|
||||
if (this.authenticationService.isAuthenticated) {
|
||||
request = request.clone({
|
||||
setHeaders: {
|
||||
Authorization: `Bearer ${this.authenticationService.getAccessToken()}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return next.handle(request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
export interface InConfiguration {
|
||||
layout: {
|
||||
rtl: boolean;
|
||||
variant: string;
|
||||
theme_color: string;
|
||||
logo_bg_color: string;
|
||||
sidebar: {
|
||||
collapsed: boolean;
|
||||
backgroundColor: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export interface GeneralErrorModel {
|
||||
//succeeded?: boolean;
|
||||
code?: string;
|
||||
description?: string;
|
||||
errors?: GeneralErrorModel[];
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { GeneralErrorModel } from './general-error.model';
|
||||
|
||||
export interface GeneralResultModel {
|
||||
success: boolean;
|
||||
error?: GeneralErrorModel;
|
||||
data?: any;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface LoginModel {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface RegisterModel {
|
||||
username: string;
|
||||
password: string;
|
||||
confirmPassword: string;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export interface UserProfileModel {
|
||||
id: string;
|
||||
email?: string;
|
||||
phone?: string;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
fullName?: string;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export interface UserModel {
|
||||
id: string;
|
||||
username: string;
|
||||
img?: string;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
//token?: string;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AuthService } from './auth.service';
|
||||
|
||||
describe('AuthService',
|
||||
() => {
|
||||
let service: AuthService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(AuthService);
|
||||
});
|
||||
|
||||
it('should be created',
|
||||
() => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,279 @@
|
||||
// angular
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { HttpHeaders } from '@angular/common/http';
|
||||
import { Router } from '@angular/router';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
// libs
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Subject } from 'rxjs';
|
||||
import { of } from 'rxjs';
|
||||
import { throwError } from 'rxjs';
|
||||
import { from } from 'rxjs';
|
||||
import { combineLatest } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
import { AuthService as Auth0Service } from '@auth0/auth0-angular';
|
||||
|
||||
import { SocialAuthService } from '@abacritt/angularx-social-login';
|
||||
import { GoogleLoginProvider } from '@abacritt/angularx-social-login';
|
||||
|
||||
// app
|
||||
import { OidcHelperService } from './oidc-helper.service';
|
||||
import { UserModel } from '../models/user.model';
|
||||
import { ApiRoutes } from '../../api-routes';
|
||||
import { RegisterModel } from '../models/register.model';
|
||||
import { LoginModel } from '../models/login.model';
|
||||
import { UserProfileModel } from '../models/user-profile.model';
|
||||
import { GeneralResultModel } from '../models/general-result.model';
|
||||
import { SubjectExtensions } from '../extensions/general.extensions';
|
||||
|
||||
export function authAppInitializerFactory(authService: AuthService): () => Promise<void> {
|
||||
if (window.location.href.indexOf('/silent-refresh.html') !== -1) {
|
||||
return () => Promise.resolve();
|
||||
}
|
||||
return () => authService.runInitialLoginSequence();
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class AuthService {
|
||||
private currentUserSubject: BehaviorSubject<UserModel>;
|
||||
currentUser$: Observable<UserModel>;
|
||||
|
||||
private isAuthenticatedSubject$ = new BehaviorSubject<boolean>(false);
|
||||
isAuthenticated$ = this.isAuthenticatedSubject$.asObservable();
|
||||
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
private router: Router,
|
||||
private route: ActivatedRoute,
|
||||
private oidcHelperService: OidcHelperService,
|
||||
private auth0Service: Auth0Service,
|
||||
private externalAuthService: SocialAuthService,
|
||||
) {
|
||||
this.currentUserSubject = new BehaviorSubject<UserModel>({} as UserModel);
|
||||
this.currentUser$ = this.currentUserSubject.asObservable();
|
||||
|
||||
// on external social login
|
||||
this.externalAuthService.authState.subscribe((user) => {
|
||||
oidcHelperService.loginByExternalLogin('google', user.idToken);
|
||||
});
|
||||
|
||||
this.oidcHelperService.isDoneLoading$.subscribe(isDone => {
|
||||
if (!isDone) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.oidcHelperService.isAuthenticated$.subscribe(authState => {
|
||||
let redirectUrl = this.getRedirect();
|
||||
if (authState.isAuthenticated) {
|
||||
this.oidcHelperService.loadUserProfile().then(e => {
|
||||
let resp = (e as any).info;
|
||||
let userProfile = {
|
||||
id: resp.id,
|
||||
email: resp.email,
|
||||
phone: resp.phone,
|
||||
firstName: resp.firstName,
|
||||
lastName: resp.lastName,
|
||||
fullName: resp.fullName,
|
||||
};
|
||||
|
||||
this.setCurrentUserValue(userProfile.id, userProfile);
|
||||
|
||||
if (authState.action === AuthenticatedActionEnum.loggedIn) {
|
||||
this.router.navigate([redirectUrl || '/dashboard/dashboard1']);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
setCurrentUserValue(id: string, profile?: UserProfileModel) {
|
||||
this.currentUserSubject.next({
|
||||
id: id,
|
||||
img: 'assets/images/user/admin.jpg',
|
||||
username: profile?.email || '',
|
||||
firstName: profile?.firstName || '',
|
||||
lastName: profile?.lastName || '',
|
||||
});
|
||||
}
|
||||
|
||||
get currentUserValue(): UserModel {
|
||||
return this.currentUserSubject.value;
|
||||
}
|
||||
|
||||
login(loginModel: LoginModel): Subject<GeneralResultModel> {
|
||||
return SubjectExtensions.start<GeneralResultModel>(subject => {
|
||||
this.oidcHelperService
|
||||
.login(loginModel.username, loginModel.password)
|
||||
.subscribe(resp => {
|
||||
if (resp.access_token) {
|
||||
this.isAuthenticatedSubject$.next(true);
|
||||
}
|
||||
subject.next({ success: !!(resp.access_token) });
|
||||
},
|
||||
err => {
|
||||
subject.next({ success: false, error: err.error });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
logout() {
|
||||
this.currentUserSubject.next({} as UserModel);
|
||||
this.isAuthenticatedSubject$.next(false);
|
||||
this.oidcHelperService.logout();
|
||||
|
||||
return of({ success: false });
|
||||
}
|
||||
|
||||
register(registerModel: RegisterModel): Observable<any> {
|
||||
return this.http
|
||||
.post<RegisterModel>(ApiRoutes.UserRegister, registerModel)
|
||||
.pipe(
|
||||
map(resp => resp),
|
||||
catchError(error => {
|
||||
return throwError(error);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
runInitialLoginSequence() {
|
||||
return this.oidcHelperService.runInitialLoginSequence();
|
||||
}
|
||||
|
||||
get isAuthenticated(): boolean {
|
||||
return this.oidcHelperService.hasValidAccessToken();
|
||||
}
|
||||
|
||||
getAccessToken() {
|
||||
return this.oidcHelperService.getAccessToken();
|
||||
}
|
||||
|
||||
loginGoogle(redirectUrl?: string): Observable<any> {
|
||||
return from(this.externalAuthService.signIn(GoogleLoginProvider.PROVIDER_ID));
|
||||
}
|
||||
|
||||
attachGoogle(): Observable<any> {
|
||||
var result = new Subject();
|
||||
|
||||
/*this.externalAuthService.signIn(GoogleLoginProvider.PROVIDER_ID).then(function (resp) {
|
||||
//console.log('attachGoogle', resp);
|
||||
//return result.next(resp);
|
||||
//this.attach(idToken.__raw, result);
|
||||
});*/
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
loginAuth0(): Observable<GeneralResultModel> {
|
||||
return SubjectExtensions.start<GeneralResultModel>((subject) => {
|
||||
var subs = this.auth0Service.idTokenClaims$.subscribe(token => {
|
||||
if (token && token?.__raw) {
|
||||
subs.unsubscribe();
|
||||
|
||||
this.oidcHelperService.loginByExternalLogin('auth0', token.__raw).subscribe(login => {
|
||||
if (login.success) {
|
||||
this.isAuthenticatedSubject$.next(true);
|
||||
}
|
||||
subject.next({ success: login.success, error: login.error });
|
||||
});
|
||||
} else {
|
||||
//subject.next({ success: false, error: { description: 'Login failed' } });
|
||||
}
|
||||
});
|
||||
|
||||
this.auth0Service.getAccessTokenWithPopup().subscribe(popupToken => {
|
||||
if (!popupToken) {
|
||||
this.auth0Service.getAccessTokenSilently().subscribe(silently => {
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
attachAuth0(): Observable<any> {
|
||||
return SubjectExtensions.start<GeneralResultModel>((subject) => {
|
||||
var subs = this.auth0Service.idTokenClaims$.subscribe(token => {
|
||||
if (token && token?.__raw) {
|
||||
subs.unsubscribe();
|
||||
|
||||
this.attach(token?.__raw).subscribe(
|
||||
(resp) => subject.next({ success: true, data: token?.__raw }),
|
||||
(err) => subject.next({ success: false, data: err })
|
||||
);
|
||||
} else {
|
||||
subject.next({ success: false, data: 'Failed' });
|
||||
}
|
||||
});
|
||||
|
||||
this.auth0Service.getAccessTokenWithPopup().subscribe(popupToken => {
|
||||
if (!popupToken) {
|
||||
this.auth0Service.getAccessTokenSilently().subscribe(silently => {
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
deattach(provider: string): Observable<any> {
|
||||
var result = new Subject();
|
||||
|
||||
var headers = new HttpHeaders({
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'Authorization': 'Bearer ' + this.getAccessToken()
|
||||
});
|
||||
this.http
|
||||
.post<any>(ApiRoutes.UserDeattach, { provider: provider }, { headers })
|
||||
.pipe(
|
||||
map(resp => resp),
|
||||
catchError(error => {
|
||||
return throwError(error);
|
||||
}))
|
||||
.subscribe((resp) => result.next(resp));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private attach(token: string): Observable<any> {
|
||||
var data = { provider: 'auth0', token: token };
|
||||
var headers = new HttpHeaders({
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'Authorization': 'Bearer ' + this.getAccessToken()
|
||||
});
|
||||
return this.http
|
||||
.post<any>(ApiRoutes.UserAttach, data, { headers });
|
||||
}
|
||||
|
||||
private getRedirect(): string | undefined {
|
||||
var redirectUrl = this.route.snapshot.queryParams['r'];
|
||||
redirectUrl = redirectUrl ? decodeURIComponent(redirectUrl) : undefined;
|
||||
return redirectUrl;
|
||||
}
|
||||
}
|
||||
|
||||
export enum AuthenticatedActionEnum {
|
||||
init,
|
||||
update,
|
||||
loggedIn,
|
||||
loggedOff,
|
||||
}
|
||||
|
||||
export interface IAuthenticatedState {
|
||||
isAuthenticated: boolean,
|
||||
action: AuthenticatedActionEnum,
|
||||
redirectUrl?: string,
|
||||
}
|
||||
|
||||
export enum StateEnum {
|
||||
undefined,
|
||||
inited,
|
||||
completed,
|
||||
failed,
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
@Injectable()
|
||||
export class DirectionService {
|
||||
private data = new BehaviorSubject('');
|
||||
currentData = this.data.asObservable();
|
||||
|
||||
constructor() {
|
||||
//constructor
|
||||
}
|
||||
|
||||
updateDirection(item: string) {
|
||||
this.data.next(item);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class LanguageService {
|
||||
languages: string[] = ['en', 'es', 'de'];
|
||||
|
||||
constructor(public translate: TranslateService) {
|
||||
let browserLang: string;
|
||||
translate.addLangs(this.languages);
|
||||
|
||||
if (localStorage.getItem('lang')) {
|
||||
browserLang = localStorage.getItem('lang') as string;
|
||||
} else {
|
||||
browserLang = translate.getBrowserLang() as string;
|
||||
}
|
||||
translate.use(browserLang.match(/en|es|de/) ? browserLang : 'en');
|
||||
}
|
||||
|
||||
setLanguage(lang: string) {
|
||||
this.translate.use(lang);
|
||||
localStorage.setItem('lang', lang);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
// angular
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { OAuthService } from 'angular-oauth2-oidc';
|
||||
import { OAuthErrorEvent } from 'angular-oauth2-oidc';
|
||||
|
||||
// libs
|
||||
import { filter, map } from 'rxjs/operators';
|
||||
import { Subject, Observable, BehaviorSubject, from } from 'rxjs';
|
||||
|
||||
// app
|
||||
import { GeneralResultModel } from '../models/general-result.model';
|
||||
|
||||
@Injectable()
|
||||
export class OidcHelperService {
|
||||
private isAuthenticatedSubject$ = new BehaviorSubject<IAuthenticatedState>({
|
||||
isAuthenticated: false,
|
||||
action: AuthenticatedActionEnum.init
|
||||
});
|
||||
isAuthenticated$ = this.isAuthenticatedSubject$.asObservable();
|
||||
|
||||
private isDoneLoadingFailed = false;
|
||||
private isDoneLoadingSubject$ = new BehaviorSubject<boolean>(false);
|
||||
isDoneLoading$ = this.isDoneLoadingSubject$.asObservable();
|
||||
|
||||
constructor(
|
||||
private oauthService: OAuthService,
|
||||
private router: Router,
|
||||
) {
|
||||
this.oauthService.oidc = false;
|
||||
|
||||
// all events handler
|
||||
this.oauthService.events.subscribe(event => {
|
||||
if (event instanceof OAuthErrorEvent) {
|
||||
console.error('OAuthErrorEvent Object:', event);
|
||||
if (event.type === 'discovery_document_validation_error') {
|
||||
this.isDoneLoadingFailed = true;
|
||||
this.setIsAuthenticatedSubject$(false, AuthenticatedActionEnum.update);
|
||||
}
|
||||
} else {
|
||||
var hasValidAccessToken = this.hasValidAccessToken();
|
||||
var isAuthenticated = this.isAuthenticatedSubject$.getValue().isAuthenticated;
|
||||
if (isAuthenticated !== hasValidAccessToken) {
|
||||
this.setIsAuthenticatedSubject$(hasValidAccessToken, AuthenticatedActionEnum.update);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// This is tricky, as it might cause race conditions (where access_token is set in another
|
||||
// tab before everything is said and done there.
|
||||
// TODO: Improve this setup. See: https://github.com/jeroenheijmans/sample-angular-oauth2-oidc-with-auth-guards/issues/2
|
||||
window.addEventListener('storage',
|
||||
(event) => {
|
||||
// The `key` is `null` if the event was caused by `.clear()`
|
||||
if (event.key !== 'access_token' && event.key !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.warn(
|
||||
'Noticed changes to access_token (most likely from another tab), updating isAuthenticated');
|
||||
this.setIsAuthenticatedSubject$(this.hasValidAccessToken(), AuthenticatedActionEnum.update);
|
||||
|
||||
if (!this.hasValidAccessToken()) {
|
||||
console.log('storage access_token updated !this.hasValidAccessToken()');
|
||||
this.logout();
|
||||
this.navigateToLoginPage();
|
||||
}
|
||||
});
|
||||
|
||||
// init login stata
|
||||
this.setIsAuthenticatedSubject$(this.hasValidAccessToken(), AuthenticatedActionEnum.update);
|
||||
|
||||
// TODO: some time receive 'message' with data 'error' == 'session_error''
|
||||
/*window.addEventListener('message', e => {
|
||||
console.log('message', e);;
|
||||
if (e.origin === 'https://dev-w08xm0pi.us.auth0.com') {
|
||||
console.log('message stopPropagation');;
|
||||
e.stopPropagation();
|
||||
}
|
||||
});
|
||||
this.oauthService.events.subscribe(x => {
|
||||
console.log('events', x);
|
||||
});*/
|
||||
this.oauthService.events
|
||||
.pipe(filter(e => ['session_terminated', 'session_error'].includes(e.type)))
|
||||
.subscribe(e => {
|
||||
console.log('events session_terminated', e, this.hasValidAccessToken());
|
||||
this.navigateToLoginPage();
|
||||
});
|
||||
|
||||
this.oauthService.setupAutomaticSilentRefresh();
|
||||
}
|
||||
|
||||
private setIsAuthenticatedSubject$(
|
||||
isAuthenticated: boolean,
|
||||
action: AuthenticatedActionEnum
|
||||
) {
|
||||
var state = this.isAuthenticatedSubject$.getValue();
|
||||
if (state.isAuthenticated !== isAuthenticated || state.action !== action
|
||||
) {
|
||||
this.isAuthenticatedSubject$.next({ isAuthenticated, action });
|
||||
}
|
||||
}
|
||||
|
||||
private navigateToLoginPage() {
|
||||
this.router.navigateByUrl('/authentication/signin');
|
||||
}
|
||||
|
||||
logout() {
|
||||
this.oauthService.logOut(true);
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.oauthService.silentRefresh();
|
||||
}
|
||||
|
||||
hasValidAccessToken() {
|
||||
return this.oauthService.hasValidAccessToken();
|
||||
}
|
||||
|
||||
getAccessToken() {
|
||||
return this.oauthService.getAccessToken();
|
||||
}
|
||||
|
||||
login(username: string, password: string) {
|
||||
//return this.oauthService.fetchTokenUsingPasswordFlow(username, password);
|
||||
return from(this.oauthService.fetchTokenUsingPasswordFlow(username, password));
|
||||
}
|
||||
|
||||
loginByExternalLogin(
|
||||
provider: string,
|
||||
token: string,
|
||||
redirectUrl?: string
|
||||
): Observable<GeneralResultModel> {
|
||||
var result = new Subject<GeneralResultModel>();
|
||||
|
||||
let params = {
|
||||
token: token,
|
||||
provider: provider,
|
||||
};
|
||||
this.oauthService
|
||||
.fetchTokenUsingGrant('external', params)
|
||||
.then(x => {
|
||||
this.setIsAuthenticatedSubject$(this.hasValidAccessToken(), AuthenticatedActionEnum.loggedIn);
|
||||
result.next({ success: true });
|
||||
})
|
||||
.catch(err => {
|
||||
result.next({ success: false, error: { code: err, description: err } });
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
loadUserProfile(): Promise<object> {
|
||||
return this.oauthService.loadUserProfile();
|
||||
}
|
||||
|
||||
runInitialLoginSequence(): Promise<void> {
|
||||
if (location.hash) {
|
||||
//console.log('Encountered hash fragment, plotting as table...');
|
||||
//console.table(location.hash.substr(1).split('&').map(kvp => kvp.split('=')));
|
||||
}
|
||||
if (this.isDoneLoadingFailed) {
|
||||
return new Promise<void>(resolve => setTimeout(() => resolve(), 1500));
|
||||
}
|
||||
// 0. LOAD CONFIG:
|
||||
// First we have to check to see how the IdServer is
|
||||
// currently configured:
|
||||
return this.oauthService.loadDiscoveryDocument()
|
||||
|
||||
// For demo purposes, we pretend the previous call was very slow
|
||||
.then(() => {
|
||||
new Promise<void>(resolve => setTimeout(() => resolve(), 1500));
|
||||
})
|
||||
|
||||
// 1. HASH LOGIN:
|
||||
// Try to log in via hash fragment after redirect back
|
||||
// from IdServer from initImplicitFlow:
|
||||
.then(() => {
|
||||
this.oauthService.tryLogin();
|
||||
})
|
||||
.then(() => {
|
||||
if (this.hasValidAccessToken()) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// 2. SILENT LOGIN:
|
||||
// Try to log in via a refresh because then we can prevent
|
||||
// needing to redirect the user:
|
||||
return this.oauthService.silentRefresh()
|
||||
.then(() => Promise.resolve())
|
||||
.catch(result => {
|
||||
// Subset of situations from https://openid.net/specs/openid-connect-core-1_0.html#AuthError
|
||||
// Only the ones where it's reasonably sure that sending the
|
||||
// user to the IdServer will help.
|
||||
const errorResponsesRequiringUserInteraction = [
|
||||
'interaction_required',
|
||||
'login_required',
|
||||
'account_selection_required',
|
||||
'consent_required',
|
||||
];
|
||||
|
||||
if (result &&
|
||||
result.reason &&
|
||||
errorResponsesRequiringUserInteraction.indexOf(result.reason.error) >= 0) {
|
||||
|
||||
// 3. ASK FOR LOGIN:
|
||||
// At this point we know for sure that we have to ask the
|
||||
// user to log in, so we redirect them to the IdServer to
|
||||
// enter credentials.
|
||||
//
|
||||
// Enable this to ALWAYS force a user to login.
|
||||
// this.login();
|
||||
//
|
||||
// Instead, we'll now do this:
|
||||
console.warn(
|
||||
'User interaction is needed to log in, we will wait for the user to manually log in.');
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// We can't handle the truth, just pass on the problem to the
|
||||
// next handler.
|
||||
return Promise.reject(result);
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
this.isDoneLoadingSubject$.next(true);
|
||||
|
||||
// Check for the strings 'undefined' and 'null' just to be sure. Our current
|
||||
// login(...) should never have this, but in case someone ever calls
|
||||
// initImplicitFlow(undefined | null) this could happen.
|
||||
if (this.oauthService.state &&
|
||||
this.oauthService.state !== 'undefined' &&
|
||||
this.oauthService.state !== 'null') {
|
||||
let stateUrl = this.oauthService.state;
|
||||
if (stateUrl.startsWith('/') === false) {
|
||||
stateUrl = decodeURIComponent(stateUrl);
|
||||
}
|
||||
console.log(`There was state of ${this.oauthService.state}, so we are sending you to: ${stateUrl}`);
|
||||
this.router.navigateByUrl(stateUrl);
|
||||
}
|
||||
})
|
||||
.catch(() => this.isDoneLoadingSubject$.next(true));
|
||||
}
|
||||
}
|
||||
|
||||
export enum AuthenticatedActionEnum {
|
||||
init,
|
||||
update,
|
||||
loggedIn,
|
||||
loggedOff,
|
||||
}
|
||||
|
||||
export interface IAuthenticatedState {
|
||||
isAuthenticated: boolean,
|
||||
action: AuthenticatedActionEnum,
|
||||
redirectUrl?: string,
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
@Injectable()
|
||||
export class RightSidebarService {
|
||||
private sidebarSubject: BehaviorSubject<boolean> = new BehaviorSubject(false);
|
||||
sidebarState = this.sidebarSubject.asObservable();
|
||||
|
||||
setRightSidebar = (value: boolean) => {
|
||||
this.sidebarSubject.next(value);
|
||||
};
|
||||
|
||||
constructor() {
|
||||
//constructor
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { Page404Component } from '../authentication/page404/page404.component';
|
||||
import { Dashboard1Component } from './dashboard1/dashboard1.component';
|
||||
import { Dashboard2Component } from './dashboard2/dashboard2.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
redirectTo: 'dashboard1',
|
||||
pathMatch: 'full',
|
||||
},
|
||||
{
|
||||
path: 'dashboard1',
|
||||
component: Dashboard1Component,
|
||||
},
|
||||
{
|
||||
path: 'dashboard2',
|
||||
component: Dashboard2Component,
|
||||
},
|
||||
{ path: '**', component: Page404Component },
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class DashboardRoutingModule {
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { DashboardRoutingModule } from './dashboard-routing.module';
|
||||
import { Dashboard1Component } from './dashboard1/dashboard1.component';
|
||||
import { Dashboard2Component } from './dashboard2/dashboard2.component';
|
||||
import { NgScrollbarModule } from 'ngx-scrollbar';
|
||||
import { NgChartsModule } from 'ng2-charts';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { NgApexchartsModule } from 'ng-apexcharts';
|
||||
import { ComponentsModule } from 'src/app/shared/components/components.module';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [Dashboard1Component, Dashboard2Component],
|
||||
imports: [
|
||||
CommonModule,
|
||||
DashboardRoutingModule,
|
||||
NgChartsModule,
|
||||
NgApexchartsModule,
|
||||
NgScrollbarModule,
|
||||
MatIconModule,
|
||||
MatButtonModule,
|
||||
MatMenuModule,
|
||||
MatTooltipModule,
|
||||
MatCheckboxModule,
|
||||
DragDropModule,
|
||||
MatProgressBarModule,
|
||||
ComponentsModule,
|
||||
SharedModule,
|
||||
],
|
||||
})
|
||||
export class DashboardModule {
|
||||
}
|
||||
@@ -0,0 +1,976 @@
|
||||
<section class="content">
|
||||
<div class="content-block">
|
||||
<div class="block-header">
|
||||
<!-- breadcrumb -->
|
||||
<app-breadcrumb [title]="'Dashboad'" [items]="['Home']" [active_item]="'Dashboad'"></app-breadcrumb>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card info-card">
|
||||
<div class="info-box8">
|
||||
<i class="material-icons bg-purple card1-icon">shopping_cart</i>
|
||||
<h5 class="col-deep-purple">Total Sales</h5>
|
||||
<h4>258</h4>
|
||||
<div>
|
||||
<i class="material-icons col-red float-start me-1 ms-1">trending_down</i>
|
||||
<p class="float-start m-0"><span class="col-orange">10%</span> Decrease
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card info-card">
|
||||
<div class="info-box8">
|
||||
<i class="material-icons bg-orange card1-icon">people</i>
|
||||
<h5 class="col-orange">Customers</h5>
|
||||
<h4>1,287</h4>
|
||||
<div>
|
||||
<i class="material-icons col-green float-start me-1 ms-1">trending_up</i>
|
||||
<p class="float-start m-0"><span class="col-green">25%</span> Increase
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card info-card">
|
||||
<div class="info-box8">
|
||||
<i class="material-icons bg-green card1-icon">local_activity</i>
|
||||
<h5 class="col-green">New Tickets</h5>
|
||||
<h4>128 </h4>
|
||||
<div>
|
||||
<i class="material-icons col-red float-start me-1 ms-1">trending_down</i>
|
||||
<p class="float-start m-0"><span class="col-orange">12%</span> Decrease
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<div class="card info-card">
|
||||
<div class="info-box8">
|
||||
<i class="material-icons bg-blue card1-icon">attach_money</i>
|
||||
<h5 class="col-blue">Revenue</h5>
|
||||
<h4>$48,697</h4>
|
||||
<div>
|
||||
<i class="material-icons col-green float-start me-1 ms-1">trending_up</i>
|
||||
<p class="float-start m-0"><span class="col-green">08%</span> Increase
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row ">
|
||||
<div class="col-xl-8 ">
|
||||
<div class="card">
|
||||
<div class="header">
|
||||
<h2>Earning Chart</h2>
|
||||
<button mat-icon-button [matMenuTriggerFor]="menu" class="header-dropdown">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item>
|
||||
<mat-icon>add_circle_outline</mat-icon>
|
||||
<span>Add</span>
|
||||
</button>
|
||||
<button mat-menu-item disabled>
|
||||
<mat-icon>delete_outline</mat-icon>
|
||||
<span>Delete</span>
|
||||
</button>
|
||||
<button mat-menu-item>
|
||||
<mat-icon>refresh</mat-icon>
|
||||
<span>Refresh</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
<div class="body p-5">
|
||||
<ul class="list-inline text-center m-b-0">
|
||||
<li class="list-inline-item p-r-30">
|
||||
<app-feather-icons [icon]="'arrow-up-circle'" [class]="'col-green'"></app-feather-icons>
|
||||
<h5 class="m-b-0">$675</h5>
|
||||
<p class="text-muted font-14 m-b-0">Weekly Earnings</p>
|
||||
</li>
|
||||
<li class="list-inline-item p-r-30">
|
||||
<app-feather-icons [icon]="'arrow-down-circle'" [class]="'col-orange'">
|
||||
</app-feather-icons>
|
||||
<h5 class="m-b-0">$1,587</h5>
|
||||
<p class="text-muted font-14 m-b-0">Monthly Earnings</p>
|
||||
</li>
|
||||
<li class="list-inline-item p-r-30">
|
||||
<app-feather-icons [icon]="'arrow-up-circle'" [class]="'col-green'"></app-feather-icons>
|
||||
<h5 class="mb-0 m-b-0">$45,965</h5>
|
||||
<p class="text-muted font-14 m-b-0">Yearly Earnings</p>
|
||||
</li>
|
||||
</ul>
|
||||
<apx-chart [series]="earningOptions.series!" [chart]="earningOptions.chart!" [xaxis]="earningOptions.xaxis!"
|
||||
[stroke]="earningOptions.stroke!" [tooltip]="earningOptions.tooltip!"
|
||||
[dataLabels]="earningOptions.dataLabels!" [legend]="earningOptions.legend!"
|
||||
[markers]="earningOptions.markers!" [grid]="earningOptions.grid!" [yaxis]="earningOptions.yaxis!"
|
||||
[title]="earningOptions.title!">
|
||||
</apx-chart>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-4">
|
||||
<div class="card">
|
||||
<div class="header">
|
||||
<h2>Transactions</h2>
|
||||
</div>
|
||||
<div class="body">
|
||||
<ng-scrollbar style="height: 380px" visibility="hover">
|
||||
<ul class="p-0 m-0">
|
||||
<li class="d-flex mb-4 pb-1">
|
||||
<div class="icon-box icon-box-green">
|
||||
<i class="material-icons col-green">account_balance</i>
|
||||
</div>
|
||||
<div class="d-flex w-100 align-items-center justify-content-between">
|
||||
<div class="me-2 ms-2">
|
||||
<small class="text-muted mb-1">Bank Transfer</small>
|
||||
<h6 class="mb-0">Send money</h6>
|
||||
</div>
|
||||
<div class="d-flex align-items-center amount-section">
|
||||
<h6 class="mb-0 col-orange">100.65</h6> <i class="material-icons col-orange">attach_money</i>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="d-flex mb-4 pb-1">
|
||||
<div class="icon-box icon-box-orange">
|
||||
<i class="material-icons col-orange">account_balance_wallet</i>
|
||||
</div>
|
||||
<div class="d-flex w-100 align-items-center justify-content-between">
|
||||
<div class="me-2 ms-2">
|
||||
<small class="text-muted mb-1">Wallet</small>
|
||||
<h6 class="mb-0">Wallet recharge</h6>
|
||||
</div>
|
||||
<div class="d-flex align-items-center amount-section">
|
||||
<h6 class="mb-0 col-green">2000</h6> <i class="material-icons col-green">attach_money</i>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="d-flex mb-4 pb-1">
|
||||
<div class="icon-box icon-box-purple">
|
||||
<i class="material-icons col-deep-purple">credit_card</i>
|
||||
</div>
|
||||
<div class="d-flex w-100 align-items-center justify-content-between">
|
||||
<div class="me-2 ms-2">
|
||||
<small class="text-muted mb-1">Credit Card</small>
|
||||
<h6 class="mb-0">Ordered Food</h6>
|
||||
</div>
|
||||
<div class="d-flex align-items-center amount-section">
|
||||
<h6 class="mb-0 col-orange">25.69</h6> <i class="material-icons col-orange">attach_money</i>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="d-flex mb-4 pb-1">
|
||||
<div class="icon-box icon-box-blue">
|
||||
<i class="material-icons col-blue">attach_money</i>
|
||||
</div>
|
||||
<div class="d-flex w-100 align-items-center justify-content-between">
|
||||
<div class="me-2 ms-2">
|
||||
<small class="text-muted mb-1">Cash Payment</small>
|
||||
<h6 class="mb-0">Sell Stationery</h6>
|
||||
</div>
|
||||
<div class="d-flex align-items-center amount-section">
|
||||
<h6 class="mb-0 col-green">148.47</h6> <i class="material-icons col-green">attach_money</i>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="d-flex mb-4 pb-1">
|
||||
<div class="icon-box icon-box-red">
|
||||
<i class="material-icons col-red">credit_card</i>
|
||||
</div>
|
||||
<div class="d-flex w-100 align-items-center justify-content-between">
|
||||
<div class="me-2 ms-2">
|
||||
<small class="text-muted mb-1">Debit Card</small>
|
||||
<h6 class="mb-0">ATM Withdraw</h6>
|
||||
</div>
|
||||
<div class="d-flex align-items-center amount-section">
|
||||
<h6 class="mb-0 col-orange">100.65</h6> <i class="material-icons col-orange">attach_money</i>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="d-flex mb-4 pb-1">
|
||||
<div class="icon-box icon-box-green">
|
||||
<i class="material-icons col-green">account_balance</i>
|
||||
</div>
|
||||
<div class="d-flex w-100 align-items-center justify-content-between">
|
||||
<div class="me-2 ms-2">
|
||||
<small class="text-muted mb-1">Bank Transfer</small>
|
||||
<h6 class="mb-0">Send money</h6>
|
||||
</div>
|
||||
<div class="d-flex align-items-center amount-section">
|
||||
<h6 class="mb-0 col-orange">100.65</h6> <i class="material-icons col-orange">attach_money</i>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</ng-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row clearfix">
|
||||
<div class="col-xs-12 col-sm-12 col-md-4 col-lg-4">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="float-start">
|
||||
<h6 class="text-muted">Average Daily Bill</h6>
|
||||
<h5>129 Dollar <span class="text-muted font-12">(Average)</span></h5>
|
||||
</div>
|
||||
<div class="mb-5">
|
||||
<apx-chart [series]="performanceRateChartOptions.series!" [chart]="performanceRateChartOptions.chart!"
|
||||
[xaxis]="performanceRateChartOptions.xaxis!" [stroke]="performanceRateChartOptions.stroke!"
|
||||
[colors]="performanceRateChartOptions.colors!" [dataLabels]="performanceRateChartOptions.dataLabels!"
|
||||
[legend]="performanceRateChartOptions.legend!" [markers]="performanceRateChartOptions.markers!"
|
||||
[grid]="performanceRateChartOptions.grid!" [yaxis]="performanceRateChartOptions.yaxis!"
|
||||
[tooltip]="performanceRateChartOptions.tooltip!" [title]="performanceRateChartOptions.title!">
|
||||
</apx-chart>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-12 col-md-8 col-lg-8">
|
||||
<div class="card">
|
||||
<div class="header">
|
||||
<h2>Invoices</h2>
|
||||
<button mat-icon-button [matMenuTriggerFor]="menu" class="header-dropdown">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item>Action</button>
|
||||
<button mat-menu-item>Another action</button>
|
||||
<button mat-menu-item>Something else here</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
<div class="tableBody">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover ">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Invoice No</th>
|
||||
<th>Client Name</th>
|
||||
<th>Due Date</th>
|
||||
<th>Status</th>
|
||||
<th>Total </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<a [routerLink]="['/admin/accounts/invoice']"> #IN7865 </a>
|
||||
</td>
|
||||
<td>
|
||||
<img src="assets/images/user/user1.jpg" alt="" class="tbl-user-img-small">John Doe
|
||||
</td>
|
||||
<td>12/05/2016 </td>
|
||||
<td>
|
||||
<div class="badge badge-solid-green">Paid</div>
|
||||
</td>
|
||||
<td>
|
||||
$500
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a [routerLink]="['/admin/accounts/invoice']"> #IN2301 </a>
|
||||
</td>
|
||||
<td>
|
||||
<img src="assets/images/user/user2.jpg" alt="" class="tbl-user-img-small">Sarah Smith
|
||||
</td>
|
||||
<td>31/03/2016 </td>
|
||||
<td>
|
||||
<div class="badge badge-solid-red">Unpaid</div>
|
||||
</td>
|
||||
<td>
|
||||
$372
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a [routerLink]="['/admin/accounts/invoice']"> #IN7239 </a>
|
||||
</td>
|
||||
<td>
|
||||
<img src="assets/images/user/user3.jpg" alt="" class="tbl-user-img-small">Airi Satou
|
||||
</td>
|
||||
<td>14/04/2017 </td>
|
||||
<td>
|
||||
<div class="badge badge-solid-orange">Partially Paid</div>
|
||||
</td>
|
||||
<td>
|
||||
$1038
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a [routerLink]="['/admin/accounts/invoice']"> #IN1482 </a>
|
||||
</td>
|
||||
<td>
|
||||
<img src="assets/images/user/user4.jpg" alt="" class="tbl-user-img-small">Angelica
|
||||
Ramos
|
||||
</td>
|
||||
<td>11/08/2017 </td>
|
||||
<td>
|
||||
<div class="badge badge-solid-green">Paid</div>
|
||||
</td>
|
||||
<td>
|
||||
$872
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a [routerLink]="['/admin/accounts/invoice']"> #IN8526 </a>
|
||||
</td>
|
||||
<td>
|
||||
<img src="assets/images/user/user5.jpg" alt="" class="tbl-user-img-small">Ashton Cox
|
||||
</td>
|
||||
<td>15/02/2018 </td>
|
||||
<td>
|
||||
<div class="badge badge-solid-red">Unpaid</div>
|
||||
</td>
|
||||
<td>
|
||||
$2398
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a [routerLink]="['/admin/accounts/invoice']"> #IN2473 </a>
|
||||
</td>
|
||||
<td>
|
||||
<img src="assets/images/user/user6.jpg" alt="" class="tbl-user-img-small">Cara Stevens
|
||||
</td>
|
||||
<td>28/01/2017 </td>
|
||||
<td>
|
||||
<div class="badge badge-solid-green">Paid</div>
|
||||
</td>
|
||||
<td>
|
||||
$834
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a [routerLink]="['/admin/accounts/invoice']"> #IN7366 </a>
|
||||
</td>
|
||||
<td>
|
||||
<img src="assets/images/user/user7.jpg" alt="" class="tbl-user-img-small">Jacob Ryan
|
||||
</td>
|
||||
<td>11/03/2017 </td>
|
||||
<td>
|
||||
<div class="badge badge-solid-orange">Partially Paid</div>
|
||||
</td>
|
||||
<td>
|
||||
$147
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row clearfix">
|
||||
<div class="col-xs-12 col-sm-12 col-md-4 col-lg-4">
|
||||
<div class="card">
|
||||
<div class="header">
|
||||
<h2>Project Status</h2>
|
||||
</div>
|
||||
<div class="body">
|
||||
<ng-scrollbar style="height: 380px" visibility="hover">
|
||||
<div class="m-2">
|
||||
<div class="row">
|
||||
<div class="col-lg-5">
|
||||
<div>Angular App</div>
|
||||
</div>
|
||||
<div class="col-lg-7">
|
||||
<div class="mt-1">
|
||||
<mat-progress-bar mode="determinate"
|
||||
class="progress-m progress-round green-progress progress-shadow" value="90">
|
||||
</mat-progress-bar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="m-2">
|
||||
<div class="row">
|
||||
<div class="col-lg-5">
|
||||
<div>Java Software</div>
|
||||
</div>
|
||||
<div class="col-lg-7">
|
||||
<div class="mt-1">
|
||||
<mat-progress-bar mode="determinate" value="54" class="progress-m progress-round red-progress">
|
||||
</mat-progress-bar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="m-2">
|
||||
<div class="row">
|
||||
<div class="col-lg-5">
|
||||
<div>Html Website</div>
|
||||
</div>
|
||||
<div class="col-lg-7">
|
||||
<div class="mt-1">
|
||||
<mat-progress-bar mode="determinate" value="68" class="progress-m progress-round sky-progress">
|
||||
</mat-progress-bar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="m-2">
|
||||
<div class="row">
|
||||
<div class="col-lg-5">
|
||||
<div>IOS App</div>
|
||||
</div>
|
||||
<div class="col-lg-7">
|
||||
<div class="mt-1">
|
||||
<mat-progress-bar mode="determinate" value="40" class="progress-m progress-round orange-progress">
|
||||
</mat-progress-bar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="m-2">
|
||||
<div class="row">
|
||||
<div class="col-lg-5">
|
||||
<div>Python Project</div>
|
||||
</div>
|
||||
<div class="col-lg-7">
|
||||
<div class="mt-1">
|
||||
<mat-progress-bar mode="determinate" value="28" class="progress-m progress-round green-progress">
|
||||
</mat-progress-bar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="m-2">
|
||||
<div class="row">
|
||||
<div class="col-lg-5">
|
||||
<div>Reactjs App</div>
|
||||
</div>
|
||||
<div class="col-lg-7">
|
||||
<div class="mt-1">
|
||||
<mat-progress-bar mode="determinate" value="89" class="progress-m progress-round sky-progress">
|
||||
</mat-progress-bar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="m-2">
|
||||
<div class="row">
|
||||
<div class="col-lg-5">
|
||||
<div>Angular App</div>
|
||||
</div>
|
||||
<div class="col-lg-7">
|
||||
<div class="mt-1">
|
||||
<mat-progress-bar mode="determinate" value="78" class="progress-m progress-round green-progress">
|
||||
</mat-progress-bar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="m-2">
|
||||
<div class="row">
|
||||
<div class="col-lg-5">
|
||||
<div>Java Software</div>
|
||||
</div>
|
||||
<div class="col-lg-7">
|
||||
<div class="mt-1">
|
||||
<mat-progress-bar mode="determinate" value="54" class="progress-m progress-round red-progress">
|
||||
</mat-progress-bar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-12 col-md-4 col-lg-4">
|
||||
<div class="card">
|
||||
<div class="header">
|
||||
<h2>Todo List</h2>
|
||||
</div>
|
||||
<div class="body">
|
||||
<ng-scrollbar style="height: 370px" visibility="hover">
|
||||
<div cdkDropList class="task-list" (cdkDropListDropped)="drop($event)">
|
||||
<div class="task-box" *ngFor="let task of tasks" cdkDrag>
|
||||
<div>
|
||||
<div class="task-handle m-r-20" cdkDragHandle>
|
||||
<mat-icon aria-hidden="false">drag_indicator</mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
<mat-checkbox (change)="toggle(task)" [checked]="!!task.done" class="m-r-15" color="primary">
|
||||
</mat-checkbox>
|
||||
<div class="task-custom-placeholder" *cdkDragPlaceholder></div>
|
||||
<div matTooltip="Title" [ngClass]="{done:task.done}">
|
||||
{{task.title}}
|
||||
</div>
|
||||
<div
|
||||
[ngClass]="{'task-low': task.priority==='Low', 'task-high': task.priority==='High','task-normal': task.priority==='Normal'}">
|
||||
<mat-icon matTooltip="Low" aria-hidden="false" class="lbl-low" *ngIf="task?.priority === 'Low'">
|
||||
arrow_downward
|
||||
</mat-icon>
|
||||
<mat-icon matTooltip="High" aria-hidden="false" class="lbl-high" *ngIf="task?.priority === 'High'">
|
||||
arrow_upward
|
||||
</mat-icon>
|
||||
<mat-icon matTooltip="Normal" aria-hidden="false" class="lbl-normal"
|
||||
*ngIf="task?.priority === 'Normal'">
|
||||
remove
|
||||
</mat-icon>
|
||||
{{task.priority}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-12 col-md-4 col-lg-4">
|
||||
<div class="card">
|
||||
<div class="header">
|
||||
<h2>Documents</h2>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="doc-file-type">
|
||||
<ng-scrollbar style="height: 380px" visibility="hover">
|
||||
<ul class="list-unstyled">
|
||||
<li class="d-flex mb-3">
|
||||
<span class="msr-3 align-self-center img-icon primary-rgba text-primary">
|
||||
<i
|
||||
class="far fa-file-word">
|
||||
</i>
|
||||
</span>
|
||||
<div class="set-flex">
|
||||
<h5 class="font-16 mb-1">Aggrement Doc</h5>
|
||||
<p>.doc, 4.3 MB</p>
|
||||
</div>
|
||||
<div class="ms-auto">
|
||||
<td>
|
||||
<i class="far fa-trash-alt psr-3"></i>
|
||||
<i class="far fa-arrow-alt-circle-down"></i>
|
||||
</td>
|
||||
</div>
|
||||
</li>
|
||||
<li class="d-flex mb-3">
|
||||
<span class="msr-3 align-self-center img-icon success-rgba text-success">
|
||||
<i
|
||||
class="far fa-file-excel">
|
||||
</i>
|
||||
</span>
|
||||
<div class="set-flex">
|
||||
<h5 class="font-16 mb-1">Stock Details</h5>
|
||||
<p>.xls, 2.5 MB</p>
|
||||
</div>
|
||||
<div class="ms-auto">
|
||||
<td>
|
||||
<i class="far fa-trash-alt psr-3"></i>
|
||||
<i class="far fa-arrow-alt-circle-down"></i>
|
||||
</td>
|
||||
</div>
|
||||
</li>
|
||||
<li class="d-flex mb-3">
|
||||
<span class="msr-3 align-self-center img-icon danger-rgba text-danger">
|
||||
<i
|
||||
class="far fa-file-pdf">
|
||||
</i>
|
||||
</span>
|
||||
<div class="set-flex">
|
||||
<h5 class="font-16 mb-1">Project Requi..</h5>
|
||||
<p>.pdf, 10.5 MB</p>
|
||||
</div>
|
||||
<div class="ms-auto">
|
||||
<td>
|
||||
<i class="far fa-trash-alt psr-3"></i>
|
||||
<i class="far fa-arrow-alt-circle-down"></i>
|
||||
</td>
|
||||
</div>
|
||||
</li>
|
||||
<li class="d-flex mb-3">
|
||||
<span class="msr-3 align-self-center img-icon info-rgba text-info">
|
||||
<i
|
||||
class="far fa-file-archive">
|
||||
</i>
|
||||
</span>
|
||||
<div class="set-flex">
|
||||
<h5 class="font-16 mb-1">Sample Code</h5>
|
||||
<p>.zip, 53.2 MB</p>
|
||||
</div>
|
||||
<div class="ms-auto">
|
||||
<td>
|
||||
<i class="far fa-trash-alt psr-3"></i>
|
||||
<i class="far fa-arrow-alt-circle-down"></i>
|
||||
</td>
|
||||
</div>
|
||||
</li>
|
||||
<li class="d-flex mb-3">
|
||||
<span class="msr-3 align-self-center img-icon primary-rgba text-primary">
|
||||
<i
|
||||
class="far fa-file-word">
|
||||
</i>
|
||||
</span>
|
||||
<div class="set-flex">
|
||||
<h5 class="font-16 mb-1">Privacy Policy</h5>
|
||||
<p>.doc, 5.3 MB</p>
|
||||
</div>
|
||||
<div class="ms-auto">
|
||||
<td>
|
||||
<i class="far fa-trash-alt psr-3"></i>
|
||||
<i class="far fa-arrow-alt-circle-down"></i>
|
||||
</td>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</ng-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12">
|
||||
<div class="card">
|
||||
<div class="header">
|
||||
<h2>Client Survay</h2>
|
||||
<button mat-icon-button [matMenuTriggerFor]="menu" class="header-dropdown">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item>Action</button>
|
||||
<button mat-menu-item>Another action</button>
|
||||
<button mat-menu-item>Something else here</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
<div class="body">
|
||||
<apx-chart [series]="areaChartOptions.series!" [chart]="areaChartOptions.chart!"
|
||||
[xaxis]="areaChartOptions.xaxis!" [yaxis]="areaChartOptions.yaxis!" [colors]="areaChartOptions.colors!"
|
||||
[stroke]="areaChartOptions.stroke!" [legend]="areaChartOptions.legend!" [grid]="areaChartOptions.grid!"
|
||||
[tooltip]="areaChartOptions.tooltip!" [dataLabels]="areaChartOptions.dataLabels!">
|
||||
</apx-chart>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12">
|
||||
<div class="card">
|
||||
<div class="header">
|
||||
<h2>Support Tickets Survay</h2>
|
||||
<button mat-icon-button [matMenuTriggerFor]="menu" class="header-dropdown">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item>Action</button>
|
||||
<button mat-menu-item>Another action</button>
|
||||
<button mat-menu-item>Something else here</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
<div class="body">
|
||||
<apx-chart [series]="barChartOptions.series!" [chart]="barChartOptions.chart!"
|
||||
[dataLabels]="barChartOptions.dataLabels!" [plotOptions]="barChartOptions.plotOptions!"
|
||||
[responsive]="barChartOptions.responsive!" [xaxis]="barChartOptions.xaxis!" [grid]="barChartOptions.grid!"
|
||||
[tooltip]="barChartOptions.tooltip!" [legend]="barChartOptions.legend!" [fill]="barChartOptions.fill!">
|
||||
</apx-chart>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row clearfix">
|
||||
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
|
||||
<div class="card">
|
||||
<div class="header">
|
||||
<h2>Projects Details</h2>
|
||||
<button mat-icon-button [matMenuTriggerFor]="menu" class="header-dropdown">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item>Action</button>
|
||||
<button mat-menu-item>Another action</button>
|
||||
<button mat-menu-item>Something else here</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
<div class="tableBody">
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Project Name</th>
|
||||
<th>Employees Team</th>
|
||||
<th>Team Leader</th>
|
||||
<th>Priority</th>
|
||||
<th>Open Task</th>
|
||||
<th>Completed Task</th>
|
||||
<th>Status</th>
|
||||
<th>Documents</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Angular App</td>
|
||||
<td class="text-truncate">
|
||||
<ul class="list-unstyled order-list">
|
||||
<li class="avatar avatar-sm">
|
||||
<img class="rounded-circle" src="assets/images/user/user1.jpg"
|
||||
alt="user">
|
||||
</li>
|
||||
<li class="avatar avatar-sm">
|
||||
<img class="rounded-circle" src="assets/images/user/user2.jpg"
|
||||
alt="user">
|
||||
</li>
|
||||
<li class="avatar avatar-sm">
|
||||
<img class="rounded-circle" src="assets/images/user/user3.jpg"
|
||||
alt="user">
|
||||
</li>
|
||||
<li class="avatar avatar-sm">
|
||||
<span class="badge">+4</span>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>John Doe</td>
|
||||
<td>
|
||||
<div class="badge col-blue">Medium</div>
|
||||
</td>
|
||||
<td>19</td>
|
||||
<td>10</td>
|
||||
<td>
|
||||
<mat-progress-bar mode="determinate" class="progress-xs progress-round green-progress" value="37">
|
||||
</mat-progress-bar>
|
||||
</td>
|
||||
<td>
|
||||
<i class="far fa-file-pdf tbl-pdf"></i>
|
||||
</td>
|
||||
<td>
|
||||
<button mat-icon-button color="accent" class="tbl-action-btn">
|
||||
<app-feather-icons [icon]="'edit'" [class]="'tbl-fav-edit'">
|
||||
</app-feather-icons>
|
||||
</button>
|
||||
<button mat-icon-button color="accent" class="tbl-action-btn">
|
||||
<app-feather-icons [icon]="'trash-2'" [class]="'tbl-fav-delete'">
|
||||
</app-feather-icons>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Java ERP</td>
|
||||
<td class="text-truncate">
|
||||
<ul class="list-unstyled order-list">
|
||||
<li class="avatar avatar-sm">
|
||||
<img class="rounded-circle" src="assets/images/user/user7.jpg"
|
||||
alt="user">
|
||||
</li>
|
||||
<li class="avatar avatar-sm">
|
||||
<img class="rounded-circle" src="assets/images/user/user2.jpg"
|
||||
alt="user">
|
||||
</li>
|
||||
<li class="avatar avatar-sm">
|
||||
<span class="badge">+3</span>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>Sarah Smith</td>
|
||||
<td>
|
||||
<div class="badge col-green">Low</div>
|
||||
</td>
|
||||
<td>25</td>
|
||||
<td>18</td>
|
||||
<td>
|
||||
<mat-progress-bar mode="determinate" class="progress-xs progress-round orange-progress"
|
||||
value="73">
|
||||
</mat-progress-bar>
|
||||
</td>
|
||||
<td>
|
||||
<i class="far fa-file-pdf tbl-pdf"></i>
|
||||
</td>
|
||||
<td>
|
||||
<button mat-icon-button color="accent" class="tbl-action-btn">
|
||||
<app-feather-icons [icon]="'edit'" [class]="'tbl-fav-edit'">
|
||||
</app-feather-icons>
|
||||
</button>
|
||||
<button mat-icon-button color="accent" class="tbl-action-btn">
|
||||
<app-feather-icons [icon]="'trash-2'" [class]="'tbl-fav-delete'">
|
||||
</app-feather-icons>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Angular App</td>
|
||||
<td class="text-truncate">
|
||||
<ul class="list-unstyled order-list">
|
||||
<li class="avatar avatar-sm">
|
||||
<img class="rounded-circle" src="assets/images/user/user2.jpg"
|
||||
alt="user">
|
||||
</li>
|
||||
<li class="avatar avatar-sm">
|
||||
<img class="rounded-circle" src="assets/images/user/user1.jpg"
|
||||
alt="user">
|
||||
</li>
|
||||
<li class="avatar avatar-sm">
|
||||
<img class="rounded-circle" src="assets/images/user/user8.jpg"
|
||||
alt="user">
|
||||
</li>
|
||||
<li class="avatar avatar-sm">
|
||||
<span class="badge">+2</span>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>Airi Satou</td>
|
||||
<td>
|
||||
<div class="badge col-blue">Medium</div>
|
||||
</td>
|
||||
<td>33</td>
|
||||
<td>21</td>
|
||||
<td>
|
||||
<mat-progress-bar mode="determinate" class="progress-xs progress-round sky-progress" value="52">
|
||||
</mat-progress-bar>
|
||||
</td>
|
||||
<td>
|
||||
<i class="far fa-file-pdf tbl-pdf"></i>
|
||||
</td>
|
||||
<td>
|
||||
<button mat-icon-button color="accent" class="tbl-action-btn">
|
||||
<app-feather-icons [icon]="'edit'" [class]="'tbl-fav-edit'">
|
||||
</app-feather-icons>
|
||||
</button>
|
||||
<button mat-icon-button color="accent" class="tbl-action-btn">
|
||||
<app-feather-icons [icon]="'trash-2'" [class]="'tbl-fav-delete'">
|
||||
</app-feather-icons>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>IOS App</td>
|
||||
<td class="text-truncate">
|
||||
<ul class="list-unstyled order-list">
|
||||
<li class="avatar avatar-sm">
|
||||
<img class="rounded-circle" src="assets/images/user/user5.jpg"
|
||||
alt="user">
|
||||
</li>
|
||||
<li class="avatar avatar-sm">
|
||||
<img class="rounded-circle" src="assets/images/user/user3.jpg"
|
||||
alt="user">
|
||||
</li>
|
||||
<li class="avatar avatar-sm">
|
||||
<img class="rounded-circle" src="assets/images/user/user9.jpg"
|
||||
alt="user">
|
||||
</li>
|
||||
<li class="avatar avatar-sm">
|
||||
<span class="badge">+4</span>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>Angelica Ramos</td>
|
||||
<td>
|
||||
<div class="badge col-orange">High</div>
|
||||
</td>
|
||||
<td>26</td>
|
||||
<td>15</td>
|
||||
<td>
|
||||
<mat-progress-bar mode="determinate" class="progress-xs progress-round red-progress" value="40">
|
||||
</mat-progress-bar>
|
||||
</td>
|
||||
<td>
|
||||
<i class="far fa-file-pdf tbl-pdf"></i>
|
||||
</td>
|
||||
<td>
|
||||
<button mat-icon-button color="accent" class="tbl-action-btn">
|
||||
<app-feather-icons [icon]="'edit'" [class]="'tbl-fav-edit'">
|
||||
</app-feather-icons>
|
||||
</button>
|
||||
<button mat-icon-button color="accent" class="tbl-action-btn">
|
||||
<app-feather-icons [icon]="'trash-2'" [class]="'tbl-fav-delete'">
|
||||
</app-feather-icons>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Wordpress</td>
|
||||
<td class="text-truncate">
|
||||
<ul class="list-unstyled order-list">
|
||||
<li class="avatar avatar-sm">
|
||||
<img class="rounded-circle" src="assets/images/user/user2.jpg"
|
||||
alt="user">
|
||||
</li>
|
||||
<li class="avatar avatar-sm">
|
||||
<img class="rounded-circle" src="assets/images/user/user4.jpg"
|
||||
alt="user">
|
||||
</li>
|
||||
<li class="avatar avatar-sm">
|
||||
<span class="badge">+3</span>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>Ashton Cox</td>
|
||||
<td>
|
||||
<div class="badge col-green">Low</div>
|
||||
</td>
|
||||
<td>12</td>
|
||||
<td>11</td>
|
||||
<td>
|
||||
<mat-progress-bar mode="determinate" class="progress-xs progress-round green-progress" value="88">
|
||||
</mat-progress-bar>
|
||||
</td>
|
||||
<td>
|
||||
<i class="far fa-file-pdf tbl-pdf"></i>
|
||||
</td>
|
||||
<td>
|
||||
<button mat-icon-button color="accent" class="tbl-action-btn">
|
||||
<app-feather-icons [icon]="'edit'" [class]="'tbl-fav-edit'">
|
||||
</app-feather-icons>
|
||||
</button>
|
||||
<button mat-icon-button color="accent" class="tbl-action-btn">
|
||||
<app-feather-icons [icon]="'trash-2'" [class]="'tbl-fav-delete'">
|
||||
</app-feather-icons>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Jasper report</td>
|
||||
<td class="text-truncate">
|
||||
<ul class="list-unstyled order-list">
|
||||
<li class="avatar avatar-sm">
|
||||
<img class="rounded-circle" src="assets/images/user/user6.jpg"
|
||||
alt="user">
|
||||
</li>
|
||||
<li class="avatar avatar-sm">
|
||||
<img class="rounded-circle" src="assets/images/user/user9.jpg"
|
||||
alt="user">
|
||||
</li>
|
||||
<li class="avatar avatar-sm">
|
||||
<img class="rounded-circle" src="assets/images/user/user4.jpg"
|
||||
alt="user">
|
||||
</li>
|
||||
<li class="avatar avatar-sm">
|
||||
<span class="badge">+2</span>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>Cara Stevens</td>
|
||||
<td>
|
||||
<div class="badge col-orange">High</div>
|
||||
</td>
|
||||
<td>43</td>
|
||||
<td>22</td>
|
||||
<td>
|
||||
<mat-progress-bar mode="determinate" class="progress-xs progress-round orange-progress"
|
||||
value="67">
|
||||
</mat-progress-bar>
|
||||
</td>
|
||||
<td>
|
||||
<i class="far fa-file-pdf tbl-pdf"></i>
|
||||
</td>
|
||||
<td>
|
||||
<button mat-icon-button color="accent" class="tbl-action-btn">
|
||||
<app-feather-icons [icon]="'edit'" [class]="'tbl-fav-edit'">
|
||||
</app-feather-icons>
|
||||
</button>
|
||||
<button mat-icon-button color="accent" class="tbl-action-btn">
|
||||
<app-feather-icons [icon]="'trash-2'" [class]="'tbl-fav-delete'">
|
||||
</app-feather-icons>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -0,0 +1,27 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { Dashboard1Component } from './dashboard1.component';
|
||||
|
||||
describe('Dashboard1Component',
|
||||
() => {
|
||||
let component: Dashboard1Component;
|
||||
let fixture: ComponentFixture<Dashboard1Component>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [Dashboard1Component]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(Dashboard1Component);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create',
|
||||
() => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,409 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import {
|
||||
ApexAxisChartSeries,
|
||||
ApexChart,
|
||||
ApexXAxis,
|
||||
ApexDataLabels,
|
||||
ApexTooltip,
|
||||
ApexYAxis,
|
||||
ApexPlotOptions,
|
||||
ApexStroke,
|
||||
ApexLegend,
|
||||
ApexFill,
|
||||
ApexMarkers,
|
||||
ApexGrid,
|
||||
ApexTitleSubtitle,
|
||||
ApexResponsive,
|
||||
} from 'ng-apexcharts';
|
||||
export type ChartOptions = {
|
||||
series: ApexAxisChartSeries;
|
||||
chart: ApexChart;
|
||||
xaxis: ApexXAxis;
|
||||
yaxis: ApexYAxis;
|
||||
stroke: ApexStroke;
|
||||
tooltip: ApexTooltip;
|
||||
dataLabels: ApexDataLabels;
|
||||
legend: ApexLegend;
|
||||
responsive: ApexResponsive[];
|
||||
plotOptions: ApexPlotOptions;
|
||||
fill: ApexFill;
|
||||
colors: string[];
|
||||
labels: string[];
|
||||
markers: ApexMarkers;
|
||||
grid: ApexGrid;
|
||||
title: ApexTitleSubtitle;
|
||||
};
|
||||
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
|
||||
|
||||
@Component({
|
||||
selector: 'app-dashboard1',
|
||||
templateUrl: './dashboard1.component.html',
|
||||
styleUrls: ['./dashboard1.component.scss'],
|
||||
})
|
||||
export class Dashboard1Component implements OnInit {
|
||||
areaChartOptions!: Partial<ChartOptions>;
|
||||
barChartOptions!: Partial<ChartOptions>;
|
||||
earningOptions!: Partial<ChartOptions>;
|
||||
performanceRateChartOptions!: Partial<ChartOptions>;
|
||||
|
||||
constructor() {
|
||||
//constructor
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.chart1();
|
||||
this.chart3();
|
||||
this.chart2();
|
||||
this.chart4();
|
||||
}
|
||||
|
||||
private chart1() {
|
||||
this.areaChartOptions = {
|
||||
series: [
|
||||
{
|
||||
name: 'New Clients',
|
||||
data: [31, 40, 28, 51, 42, 85, 77],
|
||||
},
|
||||
{
|
||||
name: 'Old Clients',
|
||||
data: [11, 32, 45, 32, 34, 52, 41],
|
||||
},
|
||||
],
|
||||
chart: {
|
||||
height: 350,
|
||||
type: 'area',
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
foreColor: '#9aa0ac',
|
||||
},
|
||||
colors: ['#4FC3F7', '#7460EE'],
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
stroke: {
|
||||
curve: 'smooth',
|
||||
},
|
||||
grid: {
|
||||
show: true,
|
||||
borderColor: '#9aa0ac',
|
||||
strokeDashArray: 1,
|
||||
},
|
||||
xaxis: {
|
||||
type: 'datetime',
|
||||
categories: [
|
||||
'2018-09-19',
|
||||
'2018-09-20',
|
||||
'2018-09-21',
|
||||
'2018-09-22',
|
||||
'2018-09-23',
|
||||
'2018-09-24',
|
||||
'2018-09-25',
|
||||
],
|
||||
},
|
||||
legend: {
|
||||
show: true,
|
||||
position: 'top',
|
||||
horizontalAlign: 'center',
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
},
|
||||
|
||||
tooltip: {
|
||||
theme: 'dark',
|
||||
marker: {
|
||||
show: true,
|
||||
},
|
||||
x: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private chart2() {
|
||||
this.barChartOptions = {
|
||||
series: [
|
||||
{
|
||||
name: 'New Errors',
|
||||
data: [44, 55, 41, 67, 22, 43],
|
||||
},
|
||||
{
|
||||
name: 'Bugs',
|
||||
data: [13, 23, 20, 8, 13, 27],
|
||||
},
|
||||
{
|
||||
name: 'Development',
|
||||
data: [11, 17, 15, 15, 21, 14],
|
||||
},
|
||||
{
|
||||
name: 'Payment',
|
||||
data: [21, 7, 25, 13, 22, 8],
|
||||
},
|
||||
],
|
||||
chart: {
|
||||
type: 'bar',
|
||||
height: 350,
|
||||
foreColor: '#9aa0ac',
|
||||
stacked: true,
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 480,
|
||||
options: {
|
||||
legend: {
|
||||
position: 'bottom',
|
||||
offsetX: -10,
|
||||
offsetY: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
plotOptions: {
|
||||
bar: {
|
||||
horizontal: false,
|
||||
columnWidth: '30%',
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
xaxis: {
|
||||
type: 'category',
|
||||
categories: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
grid: {
|
||||
show: true,
|
||||
borderColor: '#9aa0ac',
|
||||
strokeDashArray: 1,
|
||||
},
|
||||
fill: {
|
||||
opacity: 0.8,
|
||||
colors: ['#E82742', '#2F3149', '#929DB0', '#CED6D3'],
|
||||
},
|
||||
tooltip: {
|
||||
theme: 'dark',
|
||||
marker: {
|
||||
show: true,
|
||||
},
|
||||
x: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private chart3() {
|
||||
this.earningOptions = {
|
||||
series: [
|
||||
{
|
||||
name: '2019',
|
||||
data: [15, 48, 36, 20, 40, 60, 35, 20, 16, 31, 22, 11],
|
||||
},
|
||||
{
|
||||
name: '2018',
|
||||
data: [8, 22, 60, 35, 17, 24, 48, 37, 56, 22, 32, 38],
|
||||
},
|
||||
],
|
||||
chart: {
|
||||
height: 240,
|
||||
type: 'line',
|
||||
zoom: {
|
||||
enabled: false,
|
||||
},
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
stroke: {
|
||||
width: 3,
|
||||
curve: 'smooth',
|
||||
dashArray: [0, 8],
|
||||
},
|
||||
colors: ['#8793ea', '#4caf50'],
|
||||
fill: {
|
||||
opacity: [1, 0.5],
|
||||
},
|
||||
markers: {
|
||||
size: 0,
|
||||
hover: {
|
||||
sizeOffset: 6,
|
||||
},
|
||||
},
|
||||
xaxis: {
|
||||
categories: [
|
||||
'Jan',
|
||||
'Feb',
|
||||
'Mar',
|
||||
'Apr',
|
||||
'May',
|
||||
'Jun',
|
||||
'Jul',
|
||||
'Aug',
|
||||
'Sep',
|
||||
'Oct',
|
||||
'Nov',
|
||||
'Dec',
|
||||
],
|
||||
labels: {
|
||||
style: {
|
||||
colors: '#8e8da4',
|
||||
},
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
labels: {
|
||||
style: {
|
||||
colors: '#8e8da4',
|
||||
},
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
show: true,
|
||||
borderColor: '#9aa0ac',
|
||||
strokeDashArray: 1,
|
||||
},
|
||||
tooltip: {
|
||||
theme: 'dark',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private chart4() {
|
||||
this.performanceRateChartOptions = {
|
||||
series: [
|
||||
{
|
||||
name: 'Bill Amount',
|
||||
data: [113, 120, 130, 120, 125, 119, 126],
|
||||
},
|
||||
],
|
||||
chart: {
|
||||
height: 380,
|
||||
type: 'line',
|
||||
dropShadow: {
|
||||
enabled: true,
|
||||
color: '#000',
|
||||
top: 18,
|
||||
left: 7,
|
||||
blur: 10,
|
||||
opacity: 0.2,
|
||||
},
|
||||
foreColor: '#9aa0ac',
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
colors: ['#6777EF'],
|
||||
dataLabels: {
|
||||
enabled: true,
|
||||
},
|
||||
stroke: {
|
||||
curve: 'smooth',
|
||||
},
|
||||
markers: {
|
||||
size: 1,
|
||||
},
|
||||
xaxis: {
|
||||
categories: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
|
||||
title: {
|
||||
text: 'Weekday',
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
title: {
|
||||
text: 'Bill Amount($)',
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
show: true,
|
||||
borderColor: '#9aa0ac',
|
||||
strokeDashArray: 1,
|
||||
},
|
||||
tooltip: {
|
||||
theme: 'dark',
|
||||
marker: {
|
||||
show: true,
|
||||
},
|
||||
x: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// TODO start
|
||||
tasks = [
|
||||
{
|
||||
id: '1',
|
||||
title: 'Submit Science Homework',
|
||||
done: true,
|
||||
priority: 'High',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: 'Request for festivle holiday',
|
||||
done: false,
|
||||
priority: 'High',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
title: 'Order new java book',
|
||||
done: false,
|
||||
priority: 'Low',
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
title: 'Remind for lunch in hotel',
|
||||
done: true,
|
||||
priority: 'Normal',
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
title: 'Pay Hostel Fees',
|
||||
done: false,
|
||||
priority: 'High',
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
title: 'Attend Seminar On Sunday',
|
||||
done: false,
|
||||
priority: 'Normal',
|
||||
},
|
||||
{
|
||||
id: '7',
|
||||
title: 'Renew bus pass',
|
||||
done: true,
|
||||
priority: 'High',
|
||||
},
|
||||
{
|
||||
id: '8',
|
||||
title: 'Issue book in library',
|
||||
done: false,
|
||||
priority: 'High',
|
||||
},
|
||||
{
|
||||
id: '9',
|
||||
title: 'Project report submit',
|
||||
done: false,
|
||||
priority: 'Low',
|
||||
},
|
||||
];
|
||||
|
||||
drop(event: CdkDragDrop<string[]>) {
|
||||
moveItemInArray(this.tasks, event.previousIndex, event.currentIndex);
|
||||
}
|
||||
|
||||
toggle(task: { done: boolean }) {
|
||||
task.done = !task.done;
|
||||
}
|
||||
// TODO end
|
||||
}
|
||||
@@ -0,0 +1,642 @@
|
||||
<section class="content">
|
||||
<div class="content-block">
|
||||
<div class="block-header">
|
||||
<!-- breadcrumb -->
|
||||
<app-breadcrumb [title]="'Dashboad 2'" [items]="['Home']" [active_item]="'Dashboad 2'"></app-breadcrumb>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12 col-sm-6 col-md-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<h5>Todays Income</h5>
|
||||
<p class="text-muted">Income For Today</p>
|
||||
</div>
|
||||
<h3 class="text-info">$170</h3>
|
||||
</div>
|
||||
<div class="card-content mt-2">
|
||||
<div class="progress skill-progress m-b-5 w-100">
|
||||
<div class="progress-bar l-bg-purple width-per-45" role="progressbar" aria-valuenow="45"
|
||||
aria-valuemin="0" aria-valuemax="100">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mt-2">
|
||||
<p class="text-muted mb-0">Change</p>
|
||||
<p class="text-muted mb-0">75%</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 col-md-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<h5>Total Revenue</h5>
|
||||
<p class="text-muted">Total Income</p>
|
||||
</div>
|
||||
<h3 class="text-success">$120</h3>
|
||||
</div>
|
||||
<div class="card-content mt-2">
|
||||
<div class="progress skill-progress m-b-5 w-100">
|
||||
<div class="progress-bar l-bg-orange width-per-45" role="progressbar" aria-valuenow="45"
|
||||
aria-valuemin="0" aria-valuemax="100">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mt-2">
|
||||
<p class="text-muted mb-0">Change</p>
|
||||
<p class="text-muted mb-0">25%</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 col-md-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<h5>New Orders</h5>
|
||||
<p class="text-muted">Fresh New Order</p>
|
||||
</div>
|
||||
<h3 class="text-danger">15</h3>
|
||||
</div>
|
||||
<div class="card-content mt-2">
|
||||
<div class="progress skill-progress m-b-5 w-100">
|
||||
<div class="progress-bar l-bg-cyan width-per-45" role="progressbar" aria-valuenow="45" aria-valuemin="0"
|
||||
aria-valuemax="100">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between">
|
||||
<p class="text-muted mb-0">Change</p>
|
||||
<p class="text-muted mb-0">50%</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 col-md-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<h5>New Users</h5>
|
||||
<p class="text-muted">Joined New User</p>
|
||||
</div>
|
||||
<h3 class="text-secondary">12</h3>
|
||||
</div>
|
||||
<div class="card-content mt-2">
|
||||
<div class="progress skill-progress m-b-5 w-100">
|
||||
<div class="progress-bar l-bg-red width-per-45" role="progressbar" aria-valuenow="45" aria-valuemin="0"
|
||||
aria-valuemax="100">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mt-2">
|
||||
<p class="text-muted mb-0">Change</p>
|
||||
<p class="text-muted mb-0">25%</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row clearfix">
|
||||
<!-- Bar chart with line -->
|
||||
<div class="col-xl-8 col-lg-8 col-md-12 col-sm-12">
|
||||
<div class="card">
|
||||
<div class="header">
|
||||
<h2>Products Performance</h2>
|
||||
<button mat-icon-button [matMenuTriggerFor]="menu" class="header-dropdown">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item>Action</button>
|
||||
<button mat-menu-item>Another action</button>
|
||||
<button mat-menu-item>Something else here</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="recent-report__chart">
|
||||
<apx-chart [series]="lineChartOptions.series!" [chart]="lineChartOptions.chart!"
|
||||
[xaxis]="lineChartOptions.xaxis!" [stroke]="lineChartOptions.stroke!"
|
||||
[colors]="lineChartOptions.colors!" [dataLabels]="lineChartOptions.dataLabels!"
|
||||
[legend]="lineChartOptions.legend!" [tooltip]="lineChartOptions.tooltip!"
|
||||
[markers]="lineChartOptions.markers!" [grid]="lineChartOptions.grid!" [yaxis]="lineChartOptions.yaxis!"
|
||||
[title]="lineChartOptions.title!" [fill]="lineChartOptions.fill!">
|
||||
</apx-chart>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-4 col-lg-4 col-md-12 col-sm-12">
|
||||
<div class="card">
|
||||
<div class="header">
|
||||
<h2>Country Wise Clients</h2>
|
||||
<button mat-icon-button [matMenuTriggerFor]="menu" class="header-dropdown">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item>Action</button>
|
||||
<button mat-menu-item>Another action</button>
|
||||
<button mat-menu-item>Something else here</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div id="chart">
|
||||
<apx-chart [series]="pieChartOptions.series2!" [chart]="pieChartOptions.chart!"
|
||||
[labels]="pieChartOptions.labels!" [responsive]="pieChartOptions.responsive!"
|
||||
[dataLabels]="pieChartOptions.dataLabels!" [legend]="pieChartOptions.legend!" class="apex-pie-center">
|
||||
</apx-chart>
|
||||
</div>
|
||||
<div class="table-responsive m-t-15">
|
||||
<table class="table align-items-center">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><i class="fa fa-circle col-cyan msr-2"></i> India</td>
|
||||
<td>23</td>
|
||||
<td class="col-green">+32%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i class="fa fa-circle col-blue msr-2"></i>USA</td>
|
||||
<td>32</td>
|
||||
<td class="col-green">+12%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i class="fa fa-circle col-orange msr-2"></i>Shrilanka</td>
|
||||
<td>12</td>
|
||||
<td class="col-orange">-12%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i class="fa fa-circle col-green msr-2"></i>Australia</td>
|
||||
<td>32</td>
|
||||
<td class="col-green">+3%</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row clearfix">
|
||||
<div class="col-xs-12 col-sm-12 col-md-4 col-lg-4">
|
||||
<div class="card">
|
||||
<div class="header">
|
||||
<h2>Notice Board</h2>
|
||||
<button mat-icon-button [matMenuTriggerFor]="menu" class="header-dropdown">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item>Action</button>
|
||||
<button mat-menu-item>Another action</button>
|
||||
<button mat-menu-item>Something else here</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
<div class="body">
|
||||
<ng-scrollbar style="height: 380px" visibility="hover">
|
||||
<div class="recent-comment">
|
||||
<div class="notice-board">
|
||||
<div class="table-img">
|
||||
<img class="notice-object" src="assets/images/user/user6.jpg" alt="...">
|
||||
</div>
|
||||
<div class="notice-body">
|
||||
<h6 class="notice-heading col-green">Airi Satou</h6>
|
||||
<p>Lorem ipsum dolor sit amet, id quo eruditi eloquentiam.</p>
|
||||
<small class="text-muted">7 hours ago</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="notice-board">
|
||||
<div class="table-img">
|
||||
<img class="notice-object" src="assets/images/user/user4.jpg" alt="...">
|
||||
</div>
|
||||
<div class="notice-body">
|
||||
<h6 class="notice-heading color-primary col-indigo">Sarah Smith</h6>
|
||||
<p>Lorem ipsum dolor sit amet, id quo eruditi eloquentiam.</p>
|
||||
<p class="comment-date">1 hour ago</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="notice-board">
|
||||
<div class="table-img">
|
||||
<img class="notice-object" src="assets/images/user/user3.jpg" alt="...">
|
||||
</div>
|
||||
<div class="notice-body">
|
||||
<h6 class="notice-heading color-danger col-cyan">Cara Stevens</h6>
|
||||
<p>Lorem ipsum dolor sit amet, id quo eruditi eloquentiam.</p>
|
||||
<div class="comment-date">Yesterday</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="notice-board no-border">
|
||||
<div class="table-img">
|
||||
<img class="notice-object" src="assets/images/user/user7.jpg" alt="...">
|
||||
</div>
|
||||
<div class="notice-body">
|
||||
<h6 class="notice-heading color-info col-orange">Ashton Cox</h6>
|
||||
<p>Lorem ipsum dolor sit amet, id quo eruditi eloquentiam.</p>
|
||||
<div class="comment-date">Yesterday</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="notice-board">
|
||||
<div class="table-img">
|
||||
<img class="notice-object" src="assets/images/user/user9.jpg" alt="...">
|
||||
</div>
|
||||
<div class="notice-body">
|
||||
<h6 class="notice-heading color-primary col-red">Mark Hay</h6>
|
||||
<p>Lorem ipsum dolor sit amet, id quo eruditi eloquentiam.</p>
|
||||
<p class="comment-date">1 hour ago</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="notice-board">
|
||||
<div class="table-img">
|
||||
<img class="notice-object" src="assets/images/user/user8.jpg" alt="...">
|
||||
</div>
|
||||
<div class="notice-body">
|
||||
<h6 class="notice-heading color-primary col-green">Jay Pandya</h6>
|
||||
<p>Lorem ipsum dolor sit amet, id quo eruditi eloquentiam.</p>
|
||||
<p class="comment-date">3 hour ago</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-4 col-lg-4 col-md-12 col-sm-12">
|
||||
<div class="card">
|
||||
<div class="header">
|
||||
<h2>Project Status</h2>
|
||||
<button mat-icon-button [matMenuTriggerFor]="menu" class="header-dropdown">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item>Action</button>
|
||||
<button mat-menu-item>Another action</button>
|
||||
<button mat-menu-item>Something else here</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
<ng-scrollbar style="height: 410px" visibility="hover">
|
||||
<div class="tableBody">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Project Name</th>
|
||||
<th>Progress</th>
|
||||
<th>Duration</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Project A</td>
|
||||
<td>
|
||||
30%
|
||||
<mat-progress-bar mode="determinate" value="30" color="warn" class="progress-round">
|
||||
</mat-progress-bar>
|
||||
</td>
|
||||
<td>2 Months</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Project B</td>
|
||||
<td>
|
||||
55%
|
||||
<mat-progress-bar mode="determinate" class="progress-round" value="55">
|
||||
</mat-progress-bar>
|
||||
</td>
|
||||
<td>3 Months</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Project C</td>
|
||||
<td>
|
||||
67%
|
||||
<mat-progress-bar mode="determinate" class="progress-round orange-progress" value="67">
|
||||
</mat-progress-bar>
|
||||
</td>
|
||||
<td>1 Months</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Project D</td>
|
||||
<td>
|
||||
70%
|
||||
<mat-progress-bar mode="determinate" class="progress-round green-progress" value="70">
|
||||
</mat-progress-bar>
|
||||
</td>
|
||||
<td>2 Months</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Project E</td>
|
||||
<td>
|
||||
24%
|
||||
<mat-progress-bar mode="determinate" value="24" class="progress-round l-red-progress">
|
||||
</mat-progress-bar>
|
||||
</td>
|
||||
<td>3 Months</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Project F</td>
|
||||
<td>
|
||||
77%
|
||||
<mat-progress-bar mode="determinate" class="progress-round l-green-progress" value="77">
|
||||
</mat-progress-bar>
|
||||
</td>
|
||||
<td>4 Months</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Project G</td>
|
||||
<td>
|
||||
41%
|
||||
<mat-progress-bar mode="determinate" value="41" class="progress-round l-cyan-progress">
|
||||
</mat-progress-bar>
|
||||
</td>
|
||||
<td>2 Months</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Project H</td>
|
||||
<td>
|
||||
41%
|
||||
<mat-progress-bar mode="determinate" value="41" class="progress-round l-orange-progress">
|
||||
</mat-progress-bar>
|
||||
</td>
|
||||
<td>2 Months</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</ng-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-12 col-md-4 col-lg-4">
|
||||
<div class="card">
|
||||
<div class="header">
|
||||
<h2>Earning Source</h2>
|
||||
<button mat-icon-button [matMenuTriggerFor]="menu" class="header-dropdown">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item>Action</button>
|
||||
<button mat-menu-item>Another action</button>
|
||||
<button mat-menu-item>Something else here</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="totalEarning">
|
||||
<h2>$90,808</h2>
|
||||
</div>
|
||||
<div class="tab-pane body" id="skills">
|
||||
<ul class="list-unstyled">
|
||||
<li>
|
||||
<div class="mb-2">
|
||||
<span class="progress-label">envato.com</span>
|
||||
<span class="float-end progress-percent label label-info m-b-5">17%</span>
|
||||
</div>
|
||||
<div class="progress skill-progress m-b-20 w-100">
|
||||
<div class="progress-bar l-bg-green width-per-45" role="progressbar" aria-valuenow="45"
|
||||
aria-valuemin="0" aria-valuemax="100">
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="mb-2">
|
||||
<span class="float-start progress-label">google.com</span>
|
||||
<span class="float-end progress-percent label label-danger m-b-5">27%</span>
|
||||
</div>
|
||||
<div class="progress skill-progress m-b-20 w-100">
|
||||
<div class="progress-bar l-bg-purple width-per-27" role="progressbar" aria-valuenow="27"
|
||||
aria-valuemin="0" aria-valuemax="100">
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="mb-2">
|
||||
<span class="float-start progress-label">yahoo.com</span>
|
||||
<span class="float-end progress-percent label label-primary m-b-5">25%</span>
|
||||
</div>
|
||||
<div class="progress skill-progress m-b-20 w-100">
|
||||
<div class="progress-bar l-bg-orange width-per-25" role="progressbar" aria-valuenow="25"
|
||||
aria-valuemin="0" aria-valuemax="100">
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="mb-2">
|
||||
<span class="float-start progress-label">store</span>
|
||||
<span class="float-end progress-percent label label-success m-b-5">18%</span>
|
||||
</div>
|
||||
<div class="progress skill-progress m-b-20 w-100">
|
||||
<div class="progress-bar l-bg-cyan width-per-18" role="progressbar" aria-valuenow="18"
|
||||
aria-valuemin="0" aria-valuemax="100">
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="mb-2">
|
||||
<span class="float-start progress-label">Others</span>
|
||||
<span class="float-end progress-percent label label-warning m-b-5">13%</span>
|
||||
</div>
|
||||
<div class="progress skill-progress m-b-20 w-100">
|
||||
<div class="progress-bar l-bg-red width-per-13" role="progressbar" aria-valuenow="13"
|
||||
aria-valuemin="0" aria-valuemax="100">
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row clearfix">
|
||||
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
|
||||
<div class="card">
|
||||
<div class="header">
|
||||
<h2>Recent Orders</h2>
|
||||
<button mat-icon-button [matMenuTriggerFor]="menu" class="header-dropdown">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item>Action</button>
|
||||
<button mat-menu-item>Another action</button>
|
||||
<button mat-menu-item>Something else here</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
<div class="tableBody">
|
||||
<div class="table-responsive">
|
||||
<table class="table display product-overview mb-30" id="support_table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Order ID</th>
|
||||
<th>Customer Name</th>
|
||||
<th>Item</th>
|
||||
<th>Order Date</th>
|
||||
<th>Quantity</th>
|
||||
<th>Payment</th>
|
||||
<th>Status</th>
|
||||
<th>Invoice</th>
|
||||
<th>Details</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>ID7865</td>
|
||||
<td>
|
||||
<img src="assets/images/user/user1.jpg" alt="" class="tbl-user-img-small">Jens
|
||||
Brincker
|
||||
</td>
|
||||
<td>iPhone X</td>
|
||||
<td>22/05/2021</td>
|
||||
<td>2</td>
|
||||
<td>Credit Card</td>
|
||||
<td>
|
||||
<div class="badge badge-solid-green">Paid</div>
|
||||
</td>
|
||||
<td>
|
||||
<i class="far fa-file-pdf tbl-pdf"></i>
|
||||
</td>
|
||||
<td>
|
||||
<button mat-stroked-button color="primary">Details</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ID9357</td>
|
||||
<td>
|
||||
<img src="assets/images/user/user2.jpg" alt="" class="tbl-user-img-small">Mark Harry
|
||||
</td>
|
||||
<td> Pixel 2</td>
|
||||
<td>12/06/2021</td>
|
||||
<td>1</td>
|
||||
<td>COD</td>
|
||||
<td>
|
||||
<div class="badge badge-solid-orange">Unpaid</div>
|
||||
</td>
|
||||
<td>
|
||||
<i class="far fa-file-pdf tbl-pdf"></i>
|
||||
</td>
|
||||
<td>
|
||||
<button mat-stroked-button color="primary">Details</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ID3987</td>
|
||||
<td>
|
||||
<img src="assets/images/user/user3.jpg" alt="" class="tbl-user-img-small">Anthony
|
||||
Davie
|
||||
</td>
|
||||
<td>OnePlus</td>
|
||||
<td>02/02/2021</td>
|
||||
<td>5</td>
|
||||
<td>Debit Card</td>
|
||||
<td>
|
||||
<div class="badge badge-solid-blue">Pending</div>
|
||||
</td>
|
||||
<td>
|
||||
<i class="far fa-file-pdf tbl-pdf"></i>
|
||||
</td>
|
||||
<td>
|
||||
<button mat-stroked-button color="primary">Details</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ID2483</td>
|
||||
<td>
|
||||
<img src="assets/images/user/user4.jpg" alt="" class="tbl-user-img-small">David Perry
|
||||
</td>
|
||||
<td>Moto Z2</td>
|
||||
<td>10/01/2021</td>
|
||||
<td>2</td>
|
||||
<td>Credit Card</td>
|
||||
<td>
|
||||
<div class="badge badge-solid-green">Paid</div>
|
||||
</td>
|
||||
<td>
|
||||
<i class="far fa-file-pdf tbl-pdf"></i>
|
||||
</td>
|
||||
<td>
|
||||
<button mat-stroked-button color="primary">Details</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ID2986</td>
|
||||
<td>
|
||||
<img src="assets/images/user/user5.jpg" alt="" class="tbl-user-img-small">John Doe
|
||||
</td>
|
||||
<td>Samsung F62</td>
|
||||
<td>20/05/2021</td>
|
||||
<td>3</td>
|
||||
<td>Net Banking</td>
|
||||
<td>
|
||||
<div class="badge badge-solid-orange">Unpaid</div>
|
||||
</td>
|
||||
<td>
|
||||
<i class="far fa-file-pdf tbl-pdf"></i>
|
||||
</td>
|
||||
<td>
|
||||
<button mat-stroked-button color="primary">Details</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ID1267</td>
|
||||
<td>
|
||||
<img src="assets/images/user/user6.jpg" alt="" class="tbl-user-img-small">Sarah Smith
|
||||
</td>
|
||||
<td>iPhone 12</td>
|
||||
<td>10/07/2021</td>
|
||||
<td>1</td>
|
||||
<td>COD</td>
|
||||
<td>
|
||||
<div class="badge badge-solid-green">Paid</div>
|
||||
</td>
|
||||
<td>
|
||||
<i class="far fa-file-pdf tbl-pdf"></i>
|
||||
</td>
|
||||
<td>
|
||||
<button mat-stroked-button color="primary">Details</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ID3398</td>
|
||||
<td>
|
||||
<img src="assets/images/user/user7.jpg" alt="" class="tbl-user-img-small">Cara Stevens
|
||||
</td>
|
||||
<td>Moto Gs5</td>
|
||||
<td>11/04/2021</td>
|
||||
<td>2</td>
|
||||
<td>UPI Payment</td>
|
||||
<td>
|
||||
<div class="badge badge-solid-blue">Pending</div>
|
||||
</td>
|
||||
<td>
|
||||
<i class="far fa-file-pdf tbl-pdf"></i>
|
||||
</td>
|
||||
<td>
|
||||
<button mat-stroked-button color="primary">Details</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ID9965</td>
|
||||
<td>
|
||||
<img src="assets/images/user/user8.jpg" alt="" class="tbl-user-img-small">Ashton Cox
|
||||
</td>
|
||||
<td>Pixel 2</td>
|
||||
<td>14/05/2021</td>
|
||||
<td>4</td>
|
||||
<td>Credit Card</td>
|
||||
<td>
|
||||
<div class="badge badge-solid-green">Paid</div>
|
||||
</td>
|
||||
<td>
|
||||
<i class="far fa-file-pdf tbl-pdf"></i>
|
||||
</td>
|
||||
<td>
|
||||
<button mat-stroked-button color="primary">Details</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -0,0 +1,27 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { Dashboard2Component } from './dashboard2.component';
|
||||
|
||||
describe('Dashboard2Component',
|
||||
() => {
|
||||
let component: Dashboard2Component;
|
||||
let fixture: ComponentFixture<Dashboard2Component>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [Dashboard2Component]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(Dashboard2Component);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create',
|
||||
() => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,154 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import {
|
||||
ApexAxisChartSeries,
|
||||
ApexChart,
|
||||
ApexXAxis,
|
||||
ApexDataLabels,
|
||||
ApexStroke,
|
||||
ApexMarkers,
|
||||
ApexYAxis,
|
||||
ApexGrid,
|
||||
ApexTitleSubtitle,
|
||||
ApexTooltip,
|
||||
ApexLegend,
|
||||
ApexFill,
|
||||
ApexResponsive,
|
||||
ApexNonAxisChartSeries,
|
||||
} from 'ng-apexcharts';
|
||||
|
||||
export type ChartOptions = {
|
||||
series: ApexAxisChartSeries;
|
||||
series2: ApexNonAxisChartSeries;
|
||||
chart: ApexChart;
|
||||
xaxis: ApexXAxis;
|
||||
stroke: ApexStroke;
|
||||
dataLabels: ApexDataLabels;
|
||||
markers: ApexMarkers;
|
||||
colors: string[];
|
||||
yaxis: ApexYAxis;
|
||||
grid: ApexGrid;
|
||||
legend: ApexLegend;
|
||||
tooltip: ApexTooltip;
|
||||
fill: ApexFill;
|
||||
title: ApexTitleSubtitle;
|
||||
responsive: ApexResponsive[];
|
||||
labels: string[];
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: 'app-dashboard2',
|
||||
templateUrl: './dashboard2.component.html',
|
||||
styleUrls: ['./dashboard2.component.scss'],
|
||||
})
|
||||
export class Dashboard2Component implements OnInit {
|
||||
lineChartOptions!: Partial<ChartOptions>;
|
||||
pieChartOptions!: Partial<ChartOptions>;
|
||||
|
||||
// color: ["#3FA7DC", "#F6A025", "#9BC311"],
|
||||
constructor() {
|
||||
//constructor
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.chart1();
|
||||
this.chart2();
|
||||
}
|
||||
|
||||
private chart1() {
|
||||
this.lineChartOptions = {
|
||||
series: [
|
||||
{
|
||||
name: 'Product 1',
|
||||
data: [70, 200, 80, 180, 170, 105, 210],
|
||||
},
|
||||
{
|
||||
name: 'Product 2',
|
||||
data: [80, 250, 30, 120, 260, 100, 180],
|
||||
},
|
||||
{
|
||||
name: 'Product 3',
|
||||
data: [85, 130, 85, 225, 80, 190, 120],
|
||||
},
|
||||
],
|
||||
chart: {
|
||||
height: 350,
|
||||
type: 'line',
|
||||
foreColor: '#9aa0ac',
|
||||
dropShadow: {
|
||||
enabled: true,
|
||||
color: '#000',
|
||||
top: 18,
|
||||
left: 7,
|
||||
blur: 10,
|
||||
opacity: 0.2,
|
||||
},
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
colors: ['#00E396', '#775DD0', '#FEB019'],
|
||||
stroke: {
|
||||
curve: 'smooth',
|
||||
},
|
||||
grid: {
|
||||
show: true,
|
||||
borderColor: '#9aa0ac',
|
||||
strokeDashArray: 1,
|
||||
},
|
||||
markers: {
|
||||
size: 3,
|
||||
},
|
||||
xaxis: {
|
||||
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul'],
|
||||
title: {
|
||||
text: 'Month',
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
// opposite: true,
|
||||
title: {
|
||||
text: 'Sales',
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
position: 'top',
|
||||
horizontalAlign: 'right',
|
||||
floating: true,
|
||||
offsetY: -25,
|
||||
offsetX: -5,
|
||||
},
|
||||
tooltip: {
|
||||
theme: 'dark',
|
||||
marker: {
|
||||
show: true,
|
||||
},
|
||||
x: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private chart2() {
|
||||
this.pieChartOptions = {
|
||||
series2: [44, 55, 13, 43, 22],
|
||||
chart: {
|
||||
type: 'donut',
|
||||
width: 225,
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
labels: ['Science', 'Mathes', 'Economics', 'History', 'Music'],
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 480,
|
||||
options: {},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<section class="content">
|
||||
<div class="content-block">
|
||||
<div class="block-header">
|
||||
<!-- breadcrumb -->
|
||||
<app-breadcrumb [title]="'Blank'" [items]="['Home','Extra']" [active_item]="'Blank'">
|
||||
</app-breadcrumb>
|
||||
</div>
|
||||
<div class="row clearfix">
|
||||
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
|
||||
<div class="card">
|
||||
<div class="header">
|
||||
<h2><strong>Blank</strong> Page</h2>
|
||||
</div>
|
||||
<div class="body">
|
||||
<!-- Add content here -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { BlankComponent } from './blank.component';
|
||||
describe('BlankComponent',
|
||||
() => {
|
||||
let component: BlankComponent;
|
||||
let fixture: ComponentFixture<BlankComponent>;
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [BlankComponent],
|
||||
}).compileComponents();
|
||||
})
|
||||
);
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(BlankComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
it('should create',
|
||||
() => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-blank',
|
||||
templateUrl: './blank.component.html',
|
||||
styleUrls: ['./blank.component.scss'],
|
||||
})
|
||||
export class BlankComponent {
|
||||
constructor() {
|
||||
// constructor
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
import { BlankComponent } from './blank/blank.component';
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: 'blank',
|
||||
component: BlankComponent,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class ExtraPagesRoutingModule {
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ExtraPagesRoutingModule } from './extra-pages-routing.module';
|
||||
import { BlankComponent } from './blank/blank.component';
|
||||
import { MatExpansionModule } from '@angular/material/expansion';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { ComponentsModule } from '../shared/components/components.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [BlankComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
ExtraPagesRoutingModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
MatExpansionModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
MatIconModule,
|
||||
MatButtonModule,
|
||||
MatCheckboxModule,
|
||||
MatTabsModule,
|
||||
ComponentsModule,
|
||||
],
|
||||
})
|
||||
export class ExtraPagesModule {
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<div [dir]="direction">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
@@ -0,0 +1,59 @@
|
||||
import { Direction } from '@angular/cdk/bidi';
|
||||
import { Component, Inject, Renderer2 } from '@angular/core';
|
||||
import { InConfiguration } from 'src/app/core/models/config.interface';
|
||||
import { DirectionService } from 'src/app/core/service/direction.service';
|
||||
import { ConfigService } from 'src/app/config/config.service';
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
|
||||
@Component({
|
||||
selector: 'app-auth-layout',
|
||||
templateUrl: './auth-layout.component.html',
|
||||
styleUrls: [],
|
||||
})
|
||||
export class AuthLayoutComponent {
|
||||
direction!: Direction;
|
||||
config!: InConfiguration;
|
||||
|
||||
constructor(
|
||||
@Inject(DOCUMENT) private document: Document,
|
||||
private directoryService: DirectionService,
|
||||
private configService: ConfigService,
|
||||
private renderer: Renderer2
|
||||
) {
|
||||
this.config = this.configService.configData;
|
||||
this.directoryService.currentData.subscribe((currentData) => {
|
||||
if (currentData) {
|
||||
this.direction = currentData === 'ltr' ? 'ltr' : 'rtl';
|
||||
} else {
|
||||
if (localStorage.getItem('isRtl')) {
|
||||
if (localStorage.getItem('isRtl') === 'true') {
|
||||
this.direction = 'rtl';
|
||||
} else if (localStorage.getItem('isRtl') === 'false') {
|
||||
this.direction = 'ltr';
|
||||
}
|
||||
} else {
|
||||
if (this.config) {
|
||||
if (this.config.layout.rtl === true) {
|
||||
this.direction = 'rtl';
|
||||
localStorage.setItem('isRtl', 'true');
|
||||
} else {
|
||||
this.direction = 'ltr';
|
||||
localStorage.setItem('isRtl', 'false');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// set theme on startup
|
||||
if (localStorage.getItem('theme')) {
|
||||
this.renderer.removeClass(this.document.body, this.config.layout.variant);
|
||||
this.renderer.addClass(
|
||||
this.document.body,
|
||||
localStorage.getItem('theme') as string
|
||||
);
|
||||
} else {
|
||||
this.renderer.addClass(this.document.body, this.config.layout.variant);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<app-header></app-header>
|
||||
<app-sidebar></app-sidebar>
|
||||
<app-right-sidebar></app-right-sidebar>
|
||||
<div [dir]="direction">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
@@ -0,0 +1,45 @@
|
||||
import { Direction } from '@angular/cdk/bidi';
|
||||
import { Component } from '@angular/core';
|
||||
import { InConfiguration } from 'src/app/core/models/config.interface';
|
||||
import { DirectionService } from 'src/app/core/service/direction.service';
|
||||
import { ConfigService } from 'src/app/config/config.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-main-layout',
|
||||
templateUrl: './main-layout.component.html',
|
||||
styleUrls: [],
|
||||
})
|
||||
export class MainLayoutComponent {
|
||||
direction!: Direction;
|
||||
config!: InConfiguration;
|
||||
|
||||
constructor(
|
||||
private directoryService: DirectionService,
|
||||
private configService: ConfigService
|
||||
) {
|
||||
this.config = this.configService.configData;
|
||||
this.directoryService.currentData.subscribe((currentData) => {
|
||||
if (currentData) {
|
||||
this.direction = currentData === 'ltr' ? 'ltr' : 'rtl';
|
||||
} else {
|
||||
if (localStorage.getItem('isRtl')) {
|
||||
if (localStorage.getItem('isRtl') === 'true') {
|
||||
this.direction = 'rtl';
|
||||
} else if (localStorage.getItem('isRtl') === 'false') {
|
||||
this.direction = 'ltr';
|
||||
}
|
||||
} else {
|
||||
if (this.config) {
|
||||
if (this.config.layout.rtl === true) {
|
||||
this.direction = 'rtl';
|
||||
localStorage.setItem('isRtl', 'true');
|
||||
} else {
|
||||
this.direction = 'ltr';
|
||||
localStorage.setItem('isRtl', 'false');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
<nav #navbar class="navbar active">
|
||||
<div class="container-fluid">
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="float-start collapse-menu-icon">
|
||||
<li>
|
||||
<button mat-button (click)="mobileMenuSidebarOpen($event,'overlay-open')" class="sidemenu-collapse">
|
||||
<app-feather-icons [icon]="'menu'" [class]="'header-icon'"></app-feather-icons>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="float-start navbar-nav navbar-left">
|
||||
<li class="nav-item btnAppList" ngbDropdown>
|
||||
<button mat-button [matMenuTriggerFor]="appDropdownMenu" class="nav-notification-icons">
|
||||
<app-feather-icons [icon]="'grid'" [class]="'header-icon'"></app-feather-icons>
|
||||
</button>
|
||||
<mat-menu #appDropdownMenu="matMenu" class="notification-dropdown app-dropdown" xPosition="before">
|
||||
<div class="noti-list">
|
||||
<ul class="menu">
|
||||
<li class="nfc-header">
|
||||
<h5 class="mb-0">Applications</h5>
|
||||
</li>
|
||||
<li>
|
||||
<div class="row g-0 p-1">
|
||||
<div class="col-3 text-center">
|
||||
<a routerLink="/apps/chat" class="app-icons">
|
||||
<img src="assets/images/apps/chat.png">
|
||||
<p class="tx-12 mb-0">Chat</p>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-3 text-center">
|
||||
<a routerLink="/calendar" class="app-icons">
|
||||
<img src="assets/images/apps/calendar.png">
|
||||
<p class="tx-12 mb-0">Calendar</p>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-3 text-center">
|
||||
<a routerLink="/task" class="app-icons">
|
||||
<img src="assets/images/apps/task.png">
|
||||
<p class="tx-12 mb-0">Task</p>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-3 text-center">
|
||||
<a routerLink="/email/inbox" class="app-icons">
|
||||
<img src="assets/images/apps/mail.png">
|
||||
<p class="tx-12 mb-0">Mail</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-0 p-1">
|
||||
<div class="col-3 text-center">
|
||||
<a routerLink="/contacts" class="app-icons">
|
||||
<img src="assets/images/apps/contact.png">
|
||||
<p class="tx-12 mb-0">Contact</p>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-3 text-center">
|
||||
<a routerLink="/apps/support" class="app-icons">
|
||||
<img src="assets/images/apps/support.png">
|
||||
<p class="tx-12 mb-0">Support</p>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-3 text-center">
|
||||
<a routerLink="/media/gallery" class="app-icons">
|
||||
<img src="assets/images/apps/gallery.png">
|
||||
<p class="tx-12 mb-0">Gallery</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</mat-menu>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="collapse navbar-collapse" [ngClass]="isNavbarCollapsed ? '' : 'show'">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<!-- Full Screen Button -->
|
||||
<li class="fullscreen">
|
||||
<button mat-button class="nav-notification-icons" (click)="callFullscreen()">
|
||||
<app-feather-icons [icon]="'maximize'" [class]="'header-icon'"></app-feather-icons>
|
||||
</button>
|
||||
</li>
|
||||
<!-- #END# Full Screen Button -->
|
||||
<li class="nav-item">
|
||||
<button mat-button [matMenuTriggerFor]="languagemenu" class="lang-dropdown nav-notification-icons">
|
||||
<img *ngIf="flagvalue !== undefined" src="{{flagvalue}}" height="16">
|
||||
<img *ngIf="flagvalue === undefined" src="{{defaultFlag}}" height="16">
|
||||
</button>
|
||||
<mat-menu #languagemenu="matMenu" class="lang-item-menu">
|
||||
<div *ngFor="let item of listLang" class="lang-item">
|
||||
<button mat-menu-item class="dropdown-item lang-item-list"
|
||||
(click)="setLanguage(item.text, item.lang, item.flag)"
|
||||
[ngClass]="{'active': langStoreValue === item.lang}">
|
||||
<img src="{{item.flag}}" class="flag-img" height="12"> <span class="align-middle">{{item.text}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</mat-menu>
|
||||
</li>
|
||||
<!-- #START# Notifications-->
|
||||
<li>
|
||||
<button mat-button [matMenuTriggerFor]="notificationMenu" class="nav-notification-icons">
|
||||
<app-feather-icons [icon]="'bell'" [class]="'header-icon'"></app-feather-icons>
|
||||
</button>
|
||||
<mat-menu #notificationMenu="matMenu" class="nfc-menu">
|
||||
<div class="nfc-header">
|
||||
<h5 class="mb-0">Notitications</h5>
|
||||
<a class="nfc-mark-as-read">Mark all as read</a>
|
||||
</div>
|
||||
<div class="nfc-dropdown">
|
||||
<ng-scrollbar style="height: 350px" visibility="hover">
|
||||
<div class="noti-list header-menu">
|
||||
<div class="menu">
|
||||
<div>
|
||||
<button mat-menu-item *ngFor="let notification of notifications" onClick="return false;"
|
||||
[ngClass]="[notification.status]">
|
||||
<span class="table-img msg-user ">
|
||||
<i class="material-icons-two-tone nfc-type-icon"
|
||||
[ngClass]="[notification.color]">
|
||||
{{notification.icon}}
|
||||
</i>
|
||||
</span>
|
||||
<span class="menu-info">
|
||||
<span class="menu-title">{{notification.message}}</span>
|
||||
<span class="menu-desc mt-2">
|
||||
<i class="material-icons">access_time</i> {{notification.time}}
|
||||
</span>
|
||||
</span>
|
||||
<span class="nfc-close">
|
||||
<app-feather-icons [icon]="'x'" [class]="'user-menu-icons'"></app-feather-icons>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-scrollbar>
|
||||
</div>
|
||||
<div class="nfc-footer">
|
||||
<a class="nfc-read-all">
|
||||
Read
|
||||
All Notifications
|
||||
</a>
|
||||
</div>
|
||||
</mat-menu>
|
||||
</li>
|
||||
<!-- #END# Notifications-->
|
||||
<li class="nav-item user_profile">
|
||||
<button mat-button [matMenuTriggerFor]="profilemenu">
|
||||
<div class="chip dropdown-toggle" ngbDropdownToggle class="">
|
||||
<span>{{userName}}</span>
|
||||
<img src="{{userImg}}" class="user_img" width="32" height="32" alt="User">
|
||||
</div>
|
||||
</button>
|
||||
<mat-menu #profilemenu="matMenu" class="profile-menu">
|
||||
<div class="noti-list">
|
||||
<div class="menu ">
|
||||
<div class="user_dw_menu">
|
||||
<button mat-menu-item routerLink="/user/profile">
|
||||
<app-feather-icons [icon]="'user'" [class]="'user-menu-icons'" routerlink="/user/profile"></app-feather-icons>Account
|
||||
</button>
|
||||
<button mat-menu-item>
|
||||
<app-feather-icons [icon]="'mail'" [class]="'user-menu-icons'"></app-feather-icons>Inbox
|
||||
</button>
|
||||
<button mat-menu-item>
|
||||
<app-feather-icons [icon]="'settings'" [class]="'user-menu-icons'"></app-feather-icons>Settings
|
||||
</button>
|
||||
<button mat-menu-item (click)="logout()">
|
||||
<app-feather-icons [icon]="'log-out'" [class]="'user-menu-icons'"></app-feather-icons>Logout
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-menu>
|
||||
</li>
|
||||
<!-- #END# Tasks -->
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { HeaderComponent } from './header.component';
|
||||
describe('HeaderComponent',
|
||||
() => {
|
||||
let component: HeaderComponent;
|
||||
let fixture: ComponentFixture<HeaderComponent>;
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [HeaderComponent],
|
||||
}).compileComponents();
|
||||
})
|
||||
);
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(HeaderComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
it('should create',
|
||||
() => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,259 @@
|
||||
// angular
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
import { Component } from '@angular/core';
|
||||
import { Inject } from '@angular/core';
|
||||
import { ElementRef } from '@angular/core';
|
||||
import { OnInit } from '@angular/core';
|
||||
import { Renderer2 } from '@angular/core';
|
||||
import { AfterViewInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
// libs
|
||||
|
||||
// app
|
||||
import { ConfigService } from 'src/app/config/config.service';
|
||||
import { InConfiguration } from 'src/app/core/models/config.interface';
|
||||
import { AuthService } from 'src/app/core/service/auth.service';
|
||||
import { LanguageService } from 'src/app/core/service/language.service';
|
||||
import { UnsubscribeOnDestroyAdapter } from 'src/app/shared/UnsubscribeOnDestroyAdapter';
|
||||
|
||||
interface Notifications {
|
||||
message: string;
|
||||
time: string;
|
||||
icon: string;
|
||||
color: string;
|
||||
status: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-header',
|
||||
templateUrl: './header.component.html',
|
||||
styleUrls: ['./header.component.scss'],
|
||||
})
|
||||
export class HeaderComponent extends UnsubscribeOnDestroyAdapter implements OnInit, AfterViewInit {
|
||||
|
||||
config!: InConfiguration;
|
||||
|
||||
userImg?: string;
|
||||
userName?: string;
|
||||
|
||||
homePage?: string;
|
||||
isNavbarCollapsed = true;
|
||||
flagvalue: string | string[] | undefined;
|
||||
countryName: string | string[] = [];
|
||||
langStoreValue?: string;
|
||||
defaultFlag?: string;
|
||||
isOpenSidebar?: boolean;
|
||||
docElement: HTMLElement | undefined;
|
||||
isFullScreen = false;
|
||||
|
||||
constructor(
|
||||
@Inject(DOCUMENT) private document: Document,
|
||||
private renderer: Renderer2,
|
||||
public elementRef: ElementRef,
|
||||
private configService: ConfigService,
|
||||
private authService: AuthService,
|
||||
private router: Router,
|
||||
public languageService: LanguageService
|
||||
) {
|
||||
super();
|
||||
|
||||
this.authService.currentUser$.subscribe(user => {
|
||||
this.setUser();
|
||||
});
|
||||
}
|
||||
|
||||
listLang = [
|
||||
{ text: 'English', flag: 'assets/images/flags/us.jpg', lang: 'en' },
|
||||
{ text: 'Spanish', flag: 'assets/images/flags/spain.jpg', lang: 'es' },
|
||||
{ text: 'German', flag: 'assets/images/flags/germany.jpg', lang: 'de' },
|
||||
];
|
||||
notifications: Notifications[] = [
|
||||
{
|
||||
message: 'Please check your mail',
|
||||
time: '14 mins ago',
|
||||
icon: 'mail',
|
||||
color: 'nfc-green',
|
||||
status: 'msg-unread',
|
||||
},
|
||||
{
|
||||
message: 'New Employee Added..',
|
||||
time: '22 mins ago',
|
||||
icon: 'person_add',
|
||||
color: 'nfc-blue',
|
||||
status: 'msg-read',
|
||||
},
|
||||
{
|
||||
message: 'Your leave is approved!! ',
|
||||
time: '3 hours ago',
|
||||
icon: 'event_available',
|
||||
color: 'nfc-orange',
|
||||
status: 'msg-read',
|
||||
},
|
||||
{
|
||||
message: 'Lets break for lunch...',
|
||||
time: '5 hours ago',
|
||||
icon: 'lunch_dining',
|
||||
color: 'nfc-blue',
|
||||
status: 'msg-read',
|
||||
},
|
||||
{
|
||||
message: 'Employee report generated',
|
||||
time: '14 mins ago',
|
||||
icon: 'description',
|
||||
color: 'nfc-green',
|
||||
status: 'msg-read',
|
||||
},
|
||||
{
|
||||
message: 'Please check your mail',
|
||||
time: '22 mins ago',
|
||||
icon: 'mail',
|
||||
color: 'nfc-red',
|
||||
status: 'msg-read',
|
||||
},
|
||||
{
|
||||
message: 'Salary credited...',
|
||||
time: '3 hours ago',
|
||||
icon: 'paid',
|
||||
color: 'nfc-purple',
|
||||
status: 'msg-read',
|
||||
},
|
||||
];
|
||||
|
||||
ngOnInit() {
|
||||
this.config = this.configService.configData;
|
||||
|
||||
this.setUser();
|
||||
|
||||
this.homePage = 'dashboard/dashboard1';
|
||||
|
||||
this.langStoreValue = localStorage.getItem('lang') as string;
|
||||
const val = this.listLang.filter((x) => x.lang === this.langStoreValue);
|
||||
this.countryName = val.map((element) => element.text);
|
||||
if (val.length === 0) {
|
||||
if (this.flagvalue === undefined) {
|
||||
this.defaultFlag = 'assets/images/flags/us.jpg';
|
||||
}
|
||||
} else {
|
||||
this.flagvalue = val.map((element) => element.flag);
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
// set theme on startup
|
||||
if (localStorage.getItem('theme')) {
|
||||
this.renderer.removeClass(this.document.body, this.config.layout.variant);
|
||||
this.renderer.addClass(
|
||||
this.document.body,
|
||||
localStorage.getItem('theme') as string
|
||||
);
|
||||
} else {
|
||||
this.renderer.addClass(this.document.body, this.config.layout.variant);
|
||||
}
|
||||
|
||||
if (localStorage.getItem('menuOption')) {
|
||||
this.renderer.addClass(
|
||||
this.document.body,
|
||||
localStorage.getItem('menuOption') as string
|
||||
);
|
||||
} else {
|
||||
this.renderer.addClass(
|
||||
this.document.body,
|
||||
'menu_' + this.config.layout.sidebar.backgroundColor
|
||||
);
|
||||
}
|
||||
|
||||
if (localStorage.getItem('choose_logoheader')) {
|
||||
this.renderer.addClass(
|
||||
this.document.body,
|
||||
localStorage.getItem('choose_logoheader') as string
|
||||
);
|
||||
} else {
|
||||
this.renderer.addClass(
|
||||
this.document.body,
|
||||
'logo-' + this.config.layout.logo_bg_color
|
||||
);
|
||||
}
|
||||
|
||||
if (localStorage.getItem('sidebar_status')) {
|
||||
if (localStorage.getItem('sidebar_status') === 'close') {
|
||||
this.renderer.addClass(this.document.body, 'side-closed');
|
||||
this.renderer.addClass(this.document.body, 'submenu-closed');
|
||||
} else {
|
||||
this.renderer.removeClass(this.document.body, 'side-closed');
|
||||
this.renderer.removeClass(this.document.body, 'submenu-closed');
|
||||
}
|
||||
} else {
|
||||
if (this.config.layout.sidebar.collapsed === true) {
|
||||
this.renderer.addClass(this.document.body, 'side-closed');
|
||||
this.renderer.addClass(this.document.body, 'submenu-closed');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
callFullscreen() {
|
||||
if (!this.isFullScreen) {
|
||||
this.docElement?.requestFullscreen();
|
||||
} else {
|
||||
document.exitFullscreen();
|
||||
}
|
||||
this.isFullScreen = !this.isFullScreen;
|
||||
}
|
||||
|
||||
setLanguage(text: string, lang: string, flag: string) {
|
||||
this.countryName = text;
|
||||
this.flagvalue = flag;
|
||||
this.langStoreValue = lang;
|
||||
this.languageService.setLanguage(lang);
|
||||
}
|
||||
|
||||
mobileMenuSidebarOpen(event: Event, className: string) {
|
||||
const hasClass = (event.target as HTMLInputElement).classList.contains(
|
||||
className
|
||||
);
|
||||
if (hasClass) {
|
||||
this.renderer.removeClass(this.document.body, className);
|
||||
} else {
|
||||
this.renderer.addClass(this.document.body, className);
|
||||
}
|
||||
|
||||
const hasClass2 = this.document.body.classList.contains('side-closed');
|
||||
if (hasClass2) {
|
||||
// this.renderer.removeClass(this.document.body, "side-closed");
|
||||
this.renderer.removeClass(this.document.body, 'submenu-closed');
|
||||
} else {
|
||||
// this.renderer.addClass(this.document.body, "side-closed");
|
||||
this.renderer.addClass(this.document.body, 'submenu-closed');
|
||||
}
|
||||
}
|
||||
|
||||
callSidemenuCollapse() {
|
||||
const hasClass = this.document.body.classList.contains('side-closed');
|
||||
if (hasClass) {
|
||||
this.renderer.removeClass(this.document.body, 'side-closed');
|
||||
this.renderer.removeClass(this.document.body, 'submenu-closed');
|
||||
} else {
|
||||
this.renderer.addClass(this.document.body, 'side-closed');
|
||||
this.renderer.addClass(this.document.body, 'submenu-closed');
|
||||
}
|
||||
}
|
||||
|
||||
logout() {
|
||||
this.subs.sink = this.authService.logout().subscribe((res) => {
|
||||
if (!res.success) {
|
||||
this.router.navigate(['/authentication/signin']);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private setUser() {
|
||||
this.userImg = this.authService.currentUserValue.img;
|
||||
|
||||
this.userName = [
|
||||
this.authService.currentUserValue.firstName,
|
||||
this.authService.currentUserValue.lastName
|
||||
].join(' ').trim();
|
||||
|
||||
this.userName = this.userName || this.authService.currentUserValue.username;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatBadgeModule } from '@angular/material/badge';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatTabsModule,
|
||||
MatMenuModule,
|
||||
MatIconModule,
|
||||
MatButtonModule,
|
||||
MatInputModule,
|
||||
MatBadgeModule,
|
||||
],
|
||||
declarations: [],
|
||||
})
|
||||
export class LayoutModule {
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
<ngx-loading-bar color="#3173D6" ref="router"></ngx-loading-bar>
|
||||
<ngx-loading-bar color="#31d662" ref="http"></ngx-loading-bar>
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { PageLoaderComponent } from './page-loader.component';
|
||||
describe('PageLoaderComponent',
|
||||
() => {
|
||||
let component: PageLoaderComponent;
|
||||
let fixture: ComponentFixture<PageLoaderComponent>;
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [PageLoaderComponent],
|
||||
}).compileComponents();
|
||||
})
|
||||
);
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PageLoaderComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
it('should create',
|
||||
() => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-page-loader',
|
||||
templateUrl: './page-loader.component.html',
|
||||
styleUrls: ['./page-loader.component.scss'],
|
||||
})
|
||||
export class PageLoaderComponent {
|
||||
constructor() {
|
||||
// constructor
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<div class="settingSidebar" [ngClass]="isOpenSidebar ? 'showSettingPanel' : ''">
|
||||
<a href="javascript:void(0)" class="settingPanelToggle" (click)="toggleRightSidebar()">
|
||||
<app-feather-icons [icon]="'settings'" [class]="'setting-sidebar-icon'"></app-feather-icons>
|
||||
</a>
|
||||
<ng-scrollbar [style.height]="maxHeight + 'px'" visibility="hover">
|
||||
<div class="settingSidebar-body ps-container ps-theme-default">
|
||||
<div class=" fade show active">
|
||||
<div class="setting-panel-header">
|
||||
Setting Panel
|
||||
</div>
|
||||
<div class="p-15 border-bottom">
|
||||
<h6 class="font-medium m-b-10">Select Layout</h6>
|
||||
<div class="flex flex-wrap hiddenradio">
|
||||
<div class="flex flex-col ">
|
||||
<label>
|
||||
<input type="radio" name="value" value="light" [checked]="isDarTheme === false ? true : false"
|
||||
(click)="lightThemeBtnClick()">
|
||||
<img src="assets/images/light.png">
|
||||
</label>
|
||||
<div class="mt-1 text-md text-center"> Light </div>
|
||||
</div>
|
||||
<div class="flex flex-col mt-3">
|
||||
<label>
|
||||
<input type="radio" name="value" value="dark" [checked]="isDarTheme === true ? true : false"
|
||||
(click)="darkThemeBtnClick()">
|
||||
<img src="assets/images/dark.png">
|
||||
</label>
|
||||
<div class="mt-1 text-md text-center"> Dark </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rightSetting">
|
||||
<h6 class="font-medium m-b-10">Sidebar Menu Color</h6>
|
||||
<mat-button-toggle-group class="mt-2" [value]="isDarkSidebar ? 'dark' : 'light'">
|
||||
<mat-button-toggle (click)="lightSidebarBtnClick()" value="light">Light</mat-button-toggle>
|
||||
<mat-button-toggle (click)="darkSidebarBtnClick()" value="dark">Dark</mat-button-toggle>
|
||||
</mat-button-toggle-group>
|
||||
</div>
|
||||
<div class="p-15 border-bottom">
|
||||
<h6 class="font-medium m-b-10">Color Theme</h6>
|
||||
<div class="theme-setting-options">
|
||||
<ul class="choose-theme list-unstyled mb-0">
|
||||
<li data-theme="white" [ngClass]="{'active': selectedBgColor === 'white'}" (click)="selectTheme('white')">
|
||||
<div class="white"></div>
|
||||
</li>
|
||||
<li data-theme="black" [ngClass]="{'active': selectedBgColor === 'black'}" (click)="selectTheme('black')">
|
||||
<div class="black"></div>
|
||||
</li>
|
||||
<li data-theme="purple" [ngClass]="{'active': selectedBgColor === 'purple'}"
|
||||
(click)="selectTheme('purple')">
|
||||
<div class="purple"></div>
|
||||
</li>
|
||||
<li data-theme="orange" [ngClass]="{'active': selectedBgColor === 'orange'}"
|
||||
(click)="selectTheme('orange')">
|
||||
<div class="orange"></div>
|
||||
</li>
|
||||
<li data-theme="cyan" [ngClass]="{'active': selectedBgColor === 'cyan'}" (click)="selectTheme('cyan')">
|
||||
<div class="cyan"></div>
|
||||
</li>
|
||||
<li data-theme="green" [ngClass]="{'active': selectedBgColor === 'green'}" (click)="selectTheme('green')">
|
||||
<div class="green"></div>
|
||||
</li>
|
||||
<li data-theme="blue" [ngClass]="{'active': selectedBgColor === 'blue'}" (click)="selectTheme('blue')">
|
||||
<div class="blue"></div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rightSetting">
|
||||
<h6 class="font-medium m-b-10">RTL Layout</h6>
|
||||
<mat-slide-toggle class="mt-2" [checked]="isRtl" (change)="switchDirection($event)"></mat-slide-toggle>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-scrollbar>
|
||||
</div>
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { RightSidebarComponent } from './right-sidebar.component';
|
||||
describe('RightSidebarComponent',
|
||||
() => {
|
||||
let component: RightSidebarComponent;
|
||||
let fixture: ComponentFixture<RightSidebarComponent>;
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [RightSidebarComponent],
|
||||
}).compileComponents();
|
||||
})
|
||||
);
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(RightSidebarComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
it('should create',
|
||||
() => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,269 @@
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
import {
|
||||
Component,
|
||||
Inject,
|
||||
ElementRef,
|
||||
OnInit,
|
||||
AfterViewInit,
|
||||
Renderer2,
|
||||
ChangeDetectionStrategy,
|
||||
} from '@angular/core';
|
||||
import { ConfigService } from 'src/app/config/config.service';
|
||||
import { RightSidebarService } from 'src/app/core/service/rightsidebar.service';
|
||||
import { MatSlideToggleChange } from '@angular/material/slide-toggle';
|
||||
import { UnsubscribeOnDestroyAdapter } from 'src/app/shared/UnsubscribeOnDestroyAdapter';
|
||||
import { DirectionService } from 'src/app/core/service/direction.service';
|
||||
import { InConfiguration } from 'src/app/core/models/config.interface';
|
||||
|
||||
@Component({
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
selector: 'app-right-sidebar',
|
||||
templateUrl: './right-sidebar.component.html',
|
||||
styleUrls: ['./right-sidebar.component.scss'],
|
||||
})
|
||||
export class RightSidebarComponent
|
||||
extends UnsubscribeOnDestroyAdapter
|
||||
implements OnInit, AfterViewInit {
|
||||
selectedBgColor = 'white';
|
||||
maxHeight!: string;
|
||||
maxWidth!: string;
|
||||
showpanel = false;
|
||||
isOpenSidebar!: boolean;
|
||||
isDarkSidebar = false;
|
||||
isDarTheme = false;
|
||||
innerHeight?: number;
|
||||
headerHeight = 60;
|
||||
isRtl = false;
|
||||
config!: InConfiguration;
|
||||
|
||||
constructor(
|
||||
@Inject(DOCUMENT) private document: Document,
|
||||
private renderer: Renderer2,
|
||||
public elementRef: ElementRef,
|
||||
private rightSidebarService: RightSidebarService,
|
||||
private configService: ConfigService,
|
||||
private directionService: DirectionService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.config = this.configService.configData;
|
||||
this.subs.sink = this.rightSidebarService.sidebarState.subscribe(
|
||||
(isRunning) => {
|
||||
this.isOpenSidebar = isRunning;
|
||||
}
|
||||
);
|
||||
this.setRightSidebarWindowHeight();
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
// set header color on startup
|
||||
if (localStorage.getItem('choose_skin')) {
|
||||
this.renderer.addClass(
|
||||
this.document.body,
|
||||
localStorage.getItem('choose_skin') as string
|
||||
);
|
||||
this.selectedBgColor = localStorage.getItem(
|
||||
'choose_skin_active'
|
||||
) as string;
|
||||
} else {
|
||||
this.renderer.addClass(
|
||||
this.document.body,
|
||||
'theme-' + this.config.layout.theme_color
|
||||
);
|
||||
this.selectedBgColor = this.config.layout.theme_color;
|
||||
}
|
||||
|
||||
if (localStorage.getItem('menuOption')) {
|
||||
if (localStorage.getItem('menuOption') === 'menu_dark') {
|
||||
this.isDarkSidebar = true;
|
||||
} else if (localStorage.getItem('menuOption') === 'menu_light') {
|
||||
this.isDarkSidebar = false;
|
||||
} else {
|
||||
this.isDarkSidebar =
|
||||
this.config.layout.sidebar.backgroundColor === 'dark' ? true : false;
|
||||
}
|
||||
} else {
|
||||
this.isDarkSidebar =
|
||||
this.config.layout.sidebar.backgroundColor === 'dark' ? true : false;
|
||||
}
|
||||
|
||||
if (localStorage.getItem('theme')) {
|
||||
if (localStorage.getItem('theme') === 'dark') {
|
||||
this.isDarTheme = true;
|
||||
} else if (localStorage.getItem('theme') === 'light') {
|
||||
this.isDarTheme = false;
|
||||
} else {
|
||||
this.isDarTheme = this.config.layout.variant === 'dark' ? true : false;
|
||||
}
|
||||
} else {
|
||||
this.isDarTheme = this.config.layout.variant === 'dark' ? true : false;
|
||||
}
|
||||
|
||||
if (localStorage.getItem('isRtl')) {
|
||||
if (localStorage.getItem('isRtl') === 'true') {
|
||||
this.setRTLSettings();
|
||||
} else if (localStorage.getItem('isRtl') === 'false') {
|
||||
this.setLTRSettings();
|
||||
}
|
||||
} else {
|
||||
if (this.config.layout.rtl == true) {
|
||||
this.setRTLSettings();
|
||||
} else {
|
||||
this.setLTRSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selectTheme(e: string) {
|
||||
this.selectedBgColor = e;
|
||||
const prevTheme = this.elementRef.nativeElement
|
||||
.querySelector('.settingSidebar .choose-theme li.active')
|
||||
.getAttribute('data-theme');
|
||||
this.renderer.removeClass(this.document.body, 'theme-' + prevTheme);
|
||||
this.renderer.addClass(this.document.body, 'theme-' + this.selectedBgColor);
|
||||
localStorage.setItem('choose_skin', 'theme-' + this.selectedBgColor);
|
||||
localStorage.setItem('choose_skin_active', this.selectedBgColor);
|
||||
}
|
||||
|
||||
lightSidebarBtnClick() {
|
||||
this.renderer.removeClass(this.document.body, 'menu_dark');
|
||||
this.renderer.removeClass(this.document.body, 'logo-black');
|
||||
this.renderer.addClass(this.document.body, 'menu_light');
|
||||
this.renderer.addClass(this.document.body, 'logo-white');
|
||||
const menuOption = 'menu_light';
|
||||
localStorage.setItem('choose_logoheader', 'logo-white');
|
||||
localStorage.setItem('menuOption', menuOption);
|
||||
}
|
||||
|
||||
darkSidebarBtnClick() {
|
||||
this.renderer.removeClass(this.document.body, 'menu_light');
|
||||
this.renderer.removeClass(this.document.body, 'logo-white');
|
||||
this.renderer.addClass(this.document.body, 'menu_dark');
|
||||
this.renderer.addClass(this.document.body, 'logo-black');
|
||||
const menuOption = 'menu_dark';
|
||||
localStorage.setItem('choose_logoheader', 'logo-black');
|
||||
localStorage.setItem('menuOption', menuOption);
|
||||
}
|
||||
|
||||
lightThemeBtnClick() {
|
||||
this.renderer.removeClass(this.document.body, 'dark');
|
||||
this.renderer.removeClass(this.document.body, 'submenu-closed');
|
||||
this.renderer.removeClass(this.document.body, 'menu_dark');
|
||||
this.renderer.removeClass(this.document.body, 'logo-black');
|
||||
if (localStorage.getItem('choose_skin')) {
|
||||
this.renderer.removeClass(
|
||||
this.document.body,
|
||||
localStorage.getItem('choose_skin') as string
|
||||
);
|
||||
} else {
|
||||
this.renderer.removeClass(
|
||||
this.document.body,
|
||||
'theme-' + this.config.layout.theme_color
|
||||
);
|
||||
}
|
||||
|
||||
this.renderer.addClass(this.document.body, 'light');
|
||||
this.renderer.addClass(this.document.body, 'submenu-closed');
|
||||
this.renderer.addClass(this.document.body, 'menu_light');
|
||||
this.renderer.addClass(this.document.body, 'logo-white');
|
||||
this.renderer.addClass(this.document.body, 'theme-white');
|
||||
const theme = 'light';
|
||||
const menuOption = 'menu_light';
|
||||
this.selectedBgColor = 'white';
|
||||
this.isDarkSidebar = false;
|
||||
localStorage.setItem('choose_logoheader', 'logo-white');
|
||||
localStorage.setItem('choose_skin', 'theme-white');
|
||||
localStorage.setItem('theme', theme);
|
||||
localStorage.setItem('menuOption', menuOption);
|
||||
}
|
||||
|
||||
darkThemeBtnClick() {
|
||||
this.renderer.removeClass(this.document.body, 'light');
|
||||
this.renderer.removeClass(this.document.body, 'submenu-closed');
|
||||
this.renderer.removeClass(this.document.body, 'menu_light');
|
||||
this.renderer.removeClass(this.document.body, 'logo-white');
|
||||
if (localStorage.getItem('choose_skin')) {
|
||||
this.renderer.removeClass(
|
||||
this.document.body,
|
||||
localStorage.getItem('choose_skin') as string
|
||||
);
|
||||
} else {
|
||||
this.renderer.removeClass(
|
||||
this.document.body,
|
||||
'theme-' + this.config.layout.theme_color
|
||||
);
|
||||
}
|
||||
this.renderer.addClass(this.document.body, 'dark');
|
||||
this.renderer.addClass(this.document.body, 'submenu-closed');
|
||||
this.renderer.addClass(this.document.body, 'menu_dark');
|
||||
this.renderer.addClass(this.document.body, 'logo-black');
|
||||
this.renderer.addClass(this.document.body, 'theme-black');
|
||||
const theme = 'dark';
|
||||
const menuOption = 'menu_dark';
|
||||
this.selectedBgColor = 'black';
|
||||
this.isDarkSidebar = true;
|
||||
localStorage.setItem('choose_logoheader', 'logo-black');
|
||||
localStorage.setItem('choose_skin', 'theme-black');
|
||||
localStorage.setItem('theme', theme);
|
||||
localStorage.setItem('menuOption', menuOption);
|
||||
}
|
||||
|
||||
setRightSidebarWindowHeight() {
|
||||
this.innerHeight = window.innerHeight;
|
||||
const height = this.innerHeight - this.headerHeight;
|
||||
this.maxHeight = height + '';
|
||||
this.maxWidth = '500px';
|
||||
}
|
||||
|
||||
onClickedOutside(event: Event) {
|
||||
const button = event.target as HTMLButtonElement;
|
||||
if (button.id !== 'settingBtn') {
|
||||
if (this.isOpenSidebar === true) {
|
||||
this.toggleRightSidebar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
toggleRightSidebar(): void {
|
||||
this.rightSidebarService.setRightSidebar(
|
||||
(this.isOpenSidebar = !this.isOpenSidebar)
|
||||
);
|
||||
}
|
||||
|
||||
switchDirection(event: MatSlideToggleChange) {
|
||||
const isrtl = String(event.checked);
|
||||
if (
|
||||
isrtl === 'false' &&
|
||||
document.getElementsByTagName('html')[0].hasAttribute('dir')
|
||||
) {
|
||||
document.getElementsByTagName('html')[0].removeAttribute('dir');
|
||||
this.renderer.removeClass(this.document.body, 'rtl');
|
||||
this.directionService.updateDirection('ltr');
|
||||
} else if (
|
||||
isrtl === 'true' &&
|
||||
!document.getElementsByTagName('html')[0].hasAttribute('dir')
|
||||
) {
|
||||
document.getElementsByTagName('html')[0].setAttribute('dir', 'rtl');
|
||||
this.renderer.addClass(this.document.body, 'rtl');
|
||||
this.directionService.updateDirection('rtl');
|
||||
}
|
||||
localStorage.setItem('isRtl', isrtl);
|
||||
this.isRtl = event.checked;
|
||||
}
|
||||
|
||||
setRTLSettings() {
|
||||
document.getElementsByTagName('html')[0].setAttribute('dir', 'rtl');
|
||||
this.renderer.addClass(this.document.body, 'rtl');
|
||||
this.isRtl = true;
|
||||
localStorage.setItem('isRtl', 'true');
|
||||
}
|
||||
|
||||
setLTRSettings() {
|
||||
document.getElementsByTagName('html')[0].removeAttribute('dir');
|
||||
this.renderer.removeClass(this.document.body, 'rtl');
|
||||
this.isRtl = false;
|
||||
localStorage.setItem('isRtl', 'false');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
import { RouteInfo } from './sidebar.metadata';
|
||||
export const ROUTES: RouteInfo[] = [
|
||||
{
|
||||
path: '',
|
||||
title: 'MENUITEMS.MAIN.TEXT',
|
||||
iconType: '',
|
||||
icon: '',
|
||||
class: '',
|
||||
groupTitle: true,
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [],
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
title: 'MENUITEMS.DASHBOARD.TEXT',
|
||||
iconType: 'feather',
|
||||
icon: 'home',
|
||||
class: 'menu-toggle',
|
||||
groupTitle: false,
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [
|
||||
{
|
||||
path: 'dashboard/dashboard1',
|
||||
title: 'MENUITEMS.DASHBOARD.LIST.DASHBOARD1',
|
||||
iconType: '',
|
||||
icon: '',
|
||||
class: 'ml-menu',
|
||||
groupTitle: false,
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [],
|
||||
},
|
||||
{
|
||||
path: 'dashboard/dashboard2',
|
||||
title: 'MENUITEMS.DASHBOARD.LIST.DASHBOARD2',
|
||||
iconType: '',
|
||||
icon: '',
|
||||
class: 'ml-menu',
|
||||
groupTitle: false,
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Common Modules
|
||||
{
|
||||
path: '',
|
||||
title: 'Authentication',
|
||||
iconType: 'feather',
|
||||
icon: 'user-check',
|
||||
class: 'menu-toggle',
|
||||
groupTitle: false,
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [
|
||||
{
|
||||
path: '/authentication/forgot-password',
|
||||
title: 'Forgot Password',
|
||||
iconType: '',
|
||||
icon: '',
|
||||
class: 'ml-menu',
|
||||
groupTitle: false,
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [],
|
||||
},
|
||||
{
|
||||
path: '/authentication/locked',
|
||||
title: 'Locked',
|
||||
iconType: '',
|
||||
icon: '',
|
||||
class: 'ml-menu',
|
||||
groupTitle: false,
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [],
|
||||
},
|
||||
{
|
||||
path: '/authentication/page404',
|
||||
title: '404 - Not Found',
|
||||
iconType: '',
|
||||
icon: '',
|
||||
class: 'ml-menu',
|
||||
groupTitle: false,
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [],
|
||||
},
|
||||
{
|
||||
path: '/authentication/page500',
|
||||
title: '500 - Server Error',
|
||||
iconType: '',
|
||||
icon: '',
|
||||
class: 'ml-menu',
|
||||
groupTitle: false,
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
title: 'Extra Pages',
|
||||
iconType: 'feather',
|
||||
icon: 'anchor',
|
||||
class: 'menu-toggle',
|
||||
groupTitle: false,
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [
|
||||
{
|
||||
path: '/extra-pages/blank',
|
||||
title: 'Blank Page',
|
||||
iconType: '',
|
||||
icon: '',
|
||||
class: 'ml-menu',
|
||||
groupTitle: false,
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
title: 'Multi level Menu',
|
||||
iconType: 'feather',
|
||||
icon: 'chevrons-down',
|
||||
class: 'menu-toggle',
|
||||
groupTitle: false,
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [
|
||||
{
|
||||
path: '/multilevel/first1',
|
||||
title: 'First',
|
||||
iconType: '',
|
||||
icon: '',
|
||||
class: 'ml-menu',
|
||||
groupTitle: false,
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [],
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
title: 'Second',
|
||||
iconType: '',
|
||||
icon: '',
|
||||
class: 'ml-sub-menu',
|
||||
groupTitle: false,
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [
|
||||
{
|
||||
path: '/multilevel/secondlevel/second1',
|
||||
title: 'Second 1',
|
||||
iconType: '',
|
||||
icon: '',
|
||||
class: 'ml-menu2',
|
||||
groupTitle: false,
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [],
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
title: 'Second 2',
|
||||
iconType: '',
|
||||
icon: '',
|
||||
class: 'ml-sub-menu2',
|
||||
groupTitle: false,
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [
|
||||
{
|
||||
path: '/multilevel/thirdlevel/third1',
|
||||
title: 'third 1',
|
||||
iconType: '',
|
||||
icon: '',
|
||||
class: 'ml-menu3',
|
||||
groupTitle: false,
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/multilevel/first3',
|
||||
title: 'Third',
|
||||
iconType: '',
|
||||
icon: '',
|
||||
class: 'ml-menu',
|
||||
groupTitle: false,
|
||||
badge: '',
|
||||
badgeClass: '',
|
||||
submenu: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,75 @@
|
||||
<div>
|
||||
<!-- Left Sidebar -->
|
||||
<aside id="leftsidebar" class="sidebar" (mouseenter)="mouseHover()" (mouseleave)="mouseOut()">
|
||||
<div class="navbar-header">
|
||||
<ul class="nav navbar-nav flex-row">
|
||||
<li class="nav-item logo">
|
||||
<a class="navbar-brand" routerLink="dashboard/dashboard1">
|
||||
<img src="assets/images/logo.png" alt=""/>
|
||||
<span class="logo-name">ase.com.ua</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item nav-toggle">
|
||||
<button mat-icon-button (click)="callSidemenuCollapse()" class="sidemenu-collapse">
|
||||
<mat-icon [ngStyle]="{'color':'#8F8C91'}" class="menuIcon">{{menuIcon}}</mat-icon>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- Menu -->
|
||||
<div class="menu">
|
||||
<ng-scrollbar [style.height]="listMaxHeight + 'px'" visibility="hover">
|
||||
<ul class="list">
|
||||
<!-- Top Most level menu -->
|
||||
<li *ngFor="let sidebarItem of sidebarItems"
|
||||
[routerLinkActive]="sidebarItem.submenu.length !== 0 ? 'active' : 'active-top'">
|
||||
<div class="header" *ngIf="sidebarItem.groupTitle === true">{{sidebarItem.title | translate}}</div>
|
||||
<a [routerLink]="sidebarItem.class === '' ? [sidebarItem.path] : null" *ngIf="!sidebarItem.groupTitle;"
|
||||
[ngClass]="[sidebarItem.class]" (click)="callToggleMenu($event, sidebarItem.submenu.length)"
|
||||
class="menu-top">
|
||||
<i-feather [name]="sidebarItem.icon" class="sidebarIcon"></i-feather>
|
||||
<span class="hide-menu">
|
||||
{{sidebarItem.title | translate}}
|
||||
</span>
|
||||
<span *ngIf="sidebarItem.badge !== '' " [ngClass]="[sidebarItem.badgeClass]">{{sidebarItem.badge}}</span>
|
||||
</a>
|
||||
<!-- First level menu -->
|
||||
<ul class="ml-menu" *ngIf="sidebarItem.submenu.length > 0">
|
||||
<li *ngFor="let sidebarSubItem1 of sidebarItem.submenu"
|
||||
[routerLinkActive]="sidebarSubItem1.submenu.length > 0 ? '' : 'active'">
|
||||
<a [routerLink]="sidebarSubItem1.submenu.length > 0 ? null : [sidebarSubItem1.path]"
|
||||
(click)="callToggleMenu($event,sidebarSubItem1.submenu.length)" [ngClass]="[sidebarSubItem1.class]">
|
||||
{{sidebarSubItem1.title | translate}}
|
||||
</a>
|
||||
<!-- Second level menu -->
|
||||
<ul class="ml-menu-2" *ngIf="sidebarSubItem1.submenu.length > 0">
|
||||
<li *ngFor="let sidebarSubItem2 of sidebarSubItem1.submenu"
|
||||
[routerLinkActive]="sidebarSubItem2.submenu.length > 0 ? '' : 'active'">
|
||||
<a [routerLink]="sidebarSubItem2.submenu.length > 0 ? null : [sidebarSubItem2.path]"
|
||||
(click)="callToggleMenu($event,sidebarSubItem2.submenu.length)"
|
||||
[ngClass]="[sidebarSubItem2.class]">
|
||||
{{sidebarSubItem2.title | translate}}
|
||||
</a>
|
||||
<!-- Third level menu -->
|
||||
<ul class="ml-menu-3" *ngIf="sidebarSubItem2.submenu.length > 0">
|
||||
<li *ngFor="let sidebarSubItem3 of sidebarSubItem2.submenu"
|
||||
[routerLinkActive]="sidebarSubItem3.submenu.length > 0 ? '' : 'active'">
|
||||
<a [routerLink]="sidebarSubItem3.submenu.length > 0 ? null : [sidebarSubItem3.path]"
|
||||
(click)="callToggleMenu($event,sidebarSubItem3.submenu.length)"
|
||||
[ngClass]="[sidebarSubItem3.class]">
|
||||
{{sidebarSubItem3.title | translate}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</ng-scrollbar>
|
||||
</div>
|
||||
<!-- #Menu -->
|
||||
</aside>
|
||||
<!-- #END# Left Sidebar -->
|
||||
</div>
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { SidebarComponent } from './sidebar.component';
|
||||
describe('SidebarComponent',
|
||||
() => {
|
||||
let component: SidebarComponent;
|
||||
let fixture: ComponentFixture<SidebarComponent>;
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [SidebarComponent],
|
||||
}).compileComponents();
|
||||
})
|
||||
);
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SidebarComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
it('should create',
|
||||
() => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,174 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
// angular
|
||||
import { Router, NavigationEnd } from '@angular/router';
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
import { Component, Inject, ElementRef, OnInit, Renderer2, HostListener, OnDestroy } from '@angular/core';
|
||||
|
||||
// app
|
||||
import { ROUTES } from './sidebar-items';
|
||||
import { AuthService } from 'src/app/core/service/auth.service';
|
||||
import { RouteInfo } from './sidebar.metadata';
|
||||
|
||||
@Component({
|
||||
selector: 'app-sidebar',
|
||||
templateUrl: './sidebar.component.html',
|
||||
styleUrls: ['./sidebar.component.scss'],
|
||||
})
|
||||
export class SidebarComponent implements OnInit, OnDestroy {
|
||||
sidebarItems!: RouteInfo[];
|
||||
innerHeight?: number;
|
||||
bodyTag!: HTMLElement;
|
||||
listMaxHeight?: string;
|
||||
listMaxWidth?: string;
|
||||
userFullName?: string;
|
||||
userImg?: string;
|
||||
userType?: string;
|
||||
headerHeight = 60;
|
||||
currentRoute?: string;
|
||||
routerObj;
|
||||
menuIcon = 'radio_button_checked';
|
||||
|
||||
constructor(
|
||||
@Inject(DOCUMENT) private document: Document,
|
||||
private renderer: Renderer2,
|
||||
public elementRef: ElementRef,
|
||||
private authService: AuthService,
|
||||
private router: Router
|
||||
) {
|
||||
this.elementRef.nativeElement.closest('body');
|
||||
this.routerObj = this.router.events.subscribe((event) => {
|
||||
if (event instanceof NavigationEnd) {
|
||||
// close sidebar on mobile screen after menu select
|
||||
this.renderer.removeClass(this.document.body, 'overlay-open');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@HostListener('window:resize', ['$event'])
|
||||
windowResizecall() {
|
||||
this.setMenuHeight();
|
||||
this.checkStatuForResize(false);
|
||||
}
|
||||
|
||||
@HostListener('document:mousedown', ['$event'])
|
||||
onGlobalClick(event: Event): void {
|
||||
if (!this.elementRef.nativeElement.contains(event.target)) {
|
||||
this.renderer.removeClass(this.document.body, 'overlay-open');
|
||||
}
|
||||
}
|
||||
|
||||
callToggleMenu(event: Event, length: number) {
|
||||
if (length > 0) {
|
||||
const parentElement = (event.target as HTMLInputElement).closest('li');
|
||||
const activeClass = parentElement?.classList.contains('active');
|
||||
|
||||
if (activeClass) {
|
||||
this.renderer.removeClass(parentElement, 'active');
|
||||
} else {
|
||||
this.renderer.addClass(parentElement, 'active');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
if (this.authService.currentUserValue) {
|
||||
this.userFullName =
|
||||
this.authService.currentUserValue.firstName +
|
||||
' ' +
|
||||
this.authService.currentUserValue.lastName;
|
||||
this.userImg = this.authService.currentUserValue.img;
|
||||
this.userType = 'Admin';
|
||||
this.sidebarItems = ROUTES.filter((sidebarItem) => sidebarItem);
|
||||
}
|
||||
|
||||
// this.sidebarItems = ROUTES.filter((sidebarItem) => sidebarItem);
|
||||
this.initLeftSidebar();
|
||||
this.bodyTag = this.document.body;
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.routerObj.unsubscribe();
|
||||
}
|
||||
|
||||
initLeftSidebar() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const _this = this;
|
||||
// Set menu height
|
||||
_this.setMenuHeight();
|
||||
_this.checkStatuForResize(true);
|
||||
}
|
||||
|
||||
setMenuHeight() {
|
||||
this.innerHeight = window.innerHeight;
|
||||
const height = this.innerHeight - this.headerHeight;
|
||||
this.listMaxHeight = height + '';
|
||||
this.listMaxWidth = '500px';
|
||||
}
|
||||
|
||||
isOpen() {
|
||||
return this.bodyTag.classList.contains('overlay-open');
|
||||
}
|
||||
|
||||
checkStatuForResize(firstTime: boolean) {
|
||||
if (window.innerWidth < 1170) {
|
||||
this.renderer.addClass(this.document.body, 'ls-closed');
|
||||
} else {
|
||||
this.renderer.removeClass(this.document.body, 'ls-closed');
|
||||
}
|
||||
}
|
||||
|
||||
mouseHover() {
|
||||
const body = this.elementRef.nativeElement.closest('body');
|
||||
if (body.classList.contains('submenu-closed')) {
|
||||
this.renderer.addClass(this.document.body, 'side-closed-hover');
|
||||
this.renderer.removeClass(this.document.body, 'submenu-closed');
|
||||
}
|
||||
}
|
||||
|
||||
mouseOut() {
|
||||
const body = this.elementRef.nativeElement.closest('body');
|
||||
if (body.classList.contains('side-closed-hover')) {
|
||||
this.renderer.removeClass(this.document.body, 'side-closed-hover');
|
||||
this.renderer.addClass(this.document.body, 'submenu-closed');
|
||||
}
|
||||
}
|
||||
|
||||
mobileMenuSidebarOpen(event: Event, className: string) {
|
||||
const hasClass = (event.target as HTMLInputElement).classList.contains(
|
||||
className
|
||||
);
|
||||
if (hasClass) {
|
||||
this.renderer.removeClass(this.document.body, className);
|
||||
} else {
|
||||
this.renderer.addClass(this.document.body, className);
|
||||
}
|
||||
}
|
||||
|
||||
callSidemenuCollapse() {
|
||||
const hasClass = this.document.body.classList.contains('side-closed');
|
||||
if (hasClass) {
|
||||
this.renderer.removeClass(this.document.body, 'side-closed');
|
||||
this.renderer.removeClass(this.document.body, 'submenu-closed');
|
||||
this.menuIcon = 'radio_button_checked';
|
||||
} else {
|
||||
this.renderer.addClass(this.document.body, 'side-closed');
|
||||
this.renderer.addClass(this.document.body, 'submenu-closed');
|
||||
this.menuIcon = 'radio_button_unchecked';
|
||||
}
|
||||
|
||||
const sideClosedHover =
|
||||
this.document.body.classList.contains('side-closed');
|
||||
if (sideClosedHover) {
|
||||
this.renderer.removeClass(this.document.body, 'side-closed-hover');
|
||||
}
|
||||
}
|
||||
|
||||
logout() {
|
||||
this.authService.logout().subscribe((res) => {
|
||||
if (!res.success) {
|
||||
console.log('sidebar.logout');
|
||||
this.router.navigate(['/authentication/signin']);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// Sidebar route metadata
|
||||
export interface RouteInfo {
|
||||
path: string;
|
||||
title: string;
|
||||
iconType: string;
|
||||
icon: string;
|
||||
class: string;
|
||||
groupTitle: boolean;
|
||||
badge: string;
|
||||
badgeClass: string;
|
||||
submenu: RouteInfo[];
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<section class="content">
|
||||
<div class="content-block">
|
||||
<div class="block-header">
|
||||
<!-- breadcrumb -->
|
||||
<app-breadcrumb [title]="'First 1'" [items]="['Home','Multilevel']" [active_item]="'First 1'">
|
||||
</app-breadcrumb>
|
||||
</div>
|
||||
<div class="row clearfix">
|
||||
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
|
||||
<div class="card">
|
||||
<div class="header">
|
||||
<h2>First Level</h2>
|
||||
</div>
|
||||
<div class="body">
|
||||
<!-- Add content here -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { First1Component } from './first1.component';
|
||||
|
||||
describe('First1Component',
|
||||
() => {
|
||||
let component: First1Component;
|
||||
let fixture: ComponentFixture<First1Component>;
|
||||
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [First1Component],
|
||||
}).compileComponents();
|
||||
})
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(First1Component);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create',
|
||||
() => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-first1',
|
||||
templateUrl: './first1.component.html',
|
||||
styleUrls: ['./first1.component.scss'],
|
||||
})
|
||||
export class First1Component {
|
||||
constructor() {
|
||||
// constructor
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user