feat(pages): implement starter page with themes

This commit is contained in:
Alex 2020-03-25 13:52:20 +03:00 committed by Sergey Andrievskiy
parent 00f0e4a2ba
commit fb6e04b80a
16 changed files with 343 additions and 184 deletions

View file

@ -55,6 +55,8 @@ import { SecurityCamerasService } from './mock/security-cameras.service';
import { RippleService } from './utils/ripple.service';
import { MockDataModule } from './mock/mock-data.module';
import { AbService } from './utils/ab.service';
import {CurrentThemeService} from './utils/theme.service';
import {ThemeGuard} from './guard/theme.guard';
const socialLinks = [
{
@ -97,6 +99,10 @@ const DATA_SERVICES = [
{provide: MAT_RIPPLE_GLOBAL_OPTIONS, useExisting: RippleService},
];
const GUARDS = [
ThemeGuard,
];
export class NbSimpleRoleProvider extends NbRoleProvider {
getRole() {
// here you could provide any role based on any auth flow
@ -107,6 +113,7 @@ export class NbSimpleRoleProvider extends NbRoleProvider {
export const NB_CORE_PROVIDERS = [
...MockDataModule.forRoot().providers,
...DATA_SERVICES,
...GUARDS,
...NbAuthModule.forRoot({
strategies: [
@ -148,6 +155,7 @@ export const NB_CORE_PROVIDERS = [
SeoService,
StateService,
AbService,
CurrentThemeService,
];
@NgModule({

View file

@ -0,0 +1,28 @@
import {Injectable} from '@angular/core';
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from '@angular/router';
import {Observable} from 'rxjs';
import {map} from 'rxjs/operators';
import {CurrentThemeService} from '../utils/theme.service';
import {NbDateService} from '@nebular/theme';
@Injectable()
export class ThemeGuard implements CanActivate {
constructor(private router: Router,
private currentThemeService: CurrentThemeService,
private dateService: NbDateService<number>) {}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot,
): Observable<boolean> | Promise<boolean> | boolean {
return this.currentThemeService.currentTheme$.pipe(
map(theme => {
const currentThemeExpiration = JSON.parse(theme).expires_in;
const currentDate = new Date().getTime();
if (!theme || currentDate > currentThemeExpiration) {
this.router.navigate(['themes']);
}
return true;
}));
}
}

View file

@ -0,0 +1,16 @@
import {Injectable, OnDestroy} from '@angular/core';
import {Observable} from 'rxjs';
import {takeWhile} from 'rxjs/operators';
@Injectable()
export class CurrentThemeService implements OnDestroy {
alive = true;
readonly currentTheme$: Observable<any> = new Observable(subscriber => {
subscriber.next(localStorage.theme);
}).pipe(takeWhile(() => this.alive));
ngOnDestroy(): void {
this.alive = false;
}
}

View file

@ -8,11 +8,12 @@ import {
NbRequestPasswordComponent,
NbResetPasswordComponent,
} from '@nebular/auth';
import {StarterScreenComponent} from './themes-screen/starter-screen.component';
import { ThemeGuard } from './@core/guard/theme.guard';
export const routes: Routes = [
{
path: 'pages',
canActivate: [ThemeGuard],
loadChildren: () => import('./pages/pages.module')
.then(m => m.PagesModule),
},
@ -24,6 +25,7 @@ export const routes: Routes = [
{
path: 'auth',
component: NbAuthComponent,
canActivate: [ThemeGuard],
children: [
{
path: '',
@ -51,8 +53,8 @@ export const routes: Routes = [
},
],
},
{ path: '', redirectTo: 'pages', pathMatch: 'full' },
{ path: '**', redirectTo: 'pages' },
{ path: '', redirectTo: 'themes', pathMatch: 'full' },
{ path: '**', redirectTo: 'themes' },
];
const config: ExtraOptions = {

View file

@ -1,65 +1,49 @@
<nb-layout windowMode>
<nb-layout-header fixed>
<!-- <div class="header-container">-->
<!-- <div class="logo-container">-->
<!-- <a class="logo" href="#">ngx-<span>admin</span></a>-->
<!-- </div>-->
<!-- </div>-->
<div class="header-container">
<div class="logo-container">
<p class="logo">ngx-<span>admin</span></p>
</div>
</div>
<!-- <div class="header-container">-->
<!-- <nb-actions size="small">-->
<!-- <nb-action class="control-item github-stars">-->
<!-- <span class="subtitle text">Support us: </span>-->
<!-- <iframe src="https://ghbtns.com/github-btn.html?user=akveo&repo=ngx-admin&type=star&count=true"-->
<!-- frameborder="0"-->
<!-- scrolling="0"-->
<!-- width="170px"-->
<!-- height="20px">-->
<!-- </iframe>-->
<!-- </nb-action>-->
<!-- <nb-action class="control-item downloads-count">-->
<!-- <nb-icon icon="download"></nb-icon>-->
<!-- <span class="subtitle number">470.000</span>-->
<!-- </nb-action>-->
<!-- <nb-action class="control-item contact-us">-->
<!-- &lt;!&ndash; (click)="trackEmailClick()"&ndash;&gt;-->
<!-- <a nbButton ghost href="mailto:contact@akveo.com" >-->
<!-- <nb-icon icon="email-outline" pack="eva"></nb-icon>-->
<!-- <span>contact@akveo.com</span>-->
<!-- </a>-->
<!-- </nb-action>-->
<!-- </nb-actions>-->
<!-- </div>-->
<div class="header-container">
<nb-actions size="small">
<nb-action class="control-item github-stars">
<span class="subtitle text">Support us: </span>
<iframe src="https://ghbtns.com/github-btn.html?user=akveo&repo=ngx-admin&type=star&count=true"
frameborder="0"
scrolling="0"
width="170px"
height="20px">
</iframe>
</nb-action>
<nb-action class="control-item downloads-count">
<nb-icon icon="download"></nb-icon>
<span class="subtitle number">470.000</span>
</nb-action>
<nb-action class="control-item contact-us" (click)="trackEmailClick()">
<a nbButton ghost href="mailto:contact@akveo.com">
<nb-icon icon="email-outline" pack="eva"></nb-icon>
<span>contact@akveo.com</span>
</a>
</nb-action>
</nb-actions>
</div>
</nb-layout-header>
<nb-layout-column class="small">
<nb-card>
<nb-card-header>Light</nb-card-header>
<nb-card-body>
<img src="assets/image/corporate-theme.png"
class="swiper-lazy"
alt="Corporate Theme" />
<nb-layout-column>
<h4>Choose theme</h4>
<ng-container *ngFor="let theme of themes">
<nb-card (click)="navigate(theme.value)">
<nb-card-header>{{theme.name}}</nb-card-header>
<nb-card-body>
<img src="../../assets/images/{{theme.value}}-theme.png"
class="theme-preview"
alt="{{theme.name}} Theme"/>
</nb-card-body>
</nb-card>
<nb-card>
<nb-card-header>Light</nb-card-header>
</nb-card>
<nb-card>
<nb-card-header>Light</nb-card-header>
</nb-card>
</nb-layout-column>
<nb-layout-column>
<nb-card>
<nb-card-header>Light</nb-card-header>
</nb-card>
<nb-card>
<nb-card-header>Light</nb-card-header>
</nb-card>
<nb-card>
<nb-card-header>Light</nb-card-header>
</nb-card>
</ng-container>
</nb-layout-column>
<nb-layout-footer fixed>

View file

@ -2,126 +2,176 @@
@import '~@nebular/theme/styles/global/breakpoints';
@import '../@theme/styles/themes';
//@include nb-install-component() {
// display: flex;
// justify-content: space-between;
// width: 100%;
//
// .logo-container {
// display: flex;
// align-items: center;
// width: calc(#{nb-theme(sidebar-width)} - #{nb-theme(header-padding)});
// }
//
// nb-action {
// height: auto;
// display: flex;
// align-content: center;
// }
//
// nb-user {
// cursor: pointer;
// }
//
// .subtitle {
// font-family: nb-theme(text-subtitle-font-family);
// font-size: nb-theme(text-subtitle-font-size);
// font-weight: nb-theme(text-subtitle-font-weight);
// line-height: nb-theme(text-subtitle-line-height);
// }
//
// .downloads-count .number {
// @include nb-ltr(margin-left, 0.5rem);
// @include nb-rtl(margin-right, 0.5rem);
// }
//
// ::ng-deep nb-search button {
// padding: 0!important;
// }
//
// .contact-us {
// padding: 0;
//
// nb-icon {
// font-size: 1.25rem;
// }
// }
//
// .header-container {
// display: flex;
// align-items: center;
// width: auto;
//
// .sidebar-toggle {
// @include nb-ltr(padding-right, 1.25rem);
// @include nb-rtl(padding-left, 1.25rem);
// text-decoration: none;
// color: nb-theme(text-hint-color);
// nb-icon {
// font-size: 1.75rem;
// }
// }
//
// .logo {
// padding: 0 1.25rem;
// font-size: 1.75rem;
// @include nb-ltr(border-left, 1px solid nb-theme(divider-color));
// @include nb-rtl(border-right, 1px solid nb-theme(divider-color));
// white-space: nowrap;
// text-decoration: none;
// }
// }
//
// .github-stars {
// width: 245px;
// display: flex;
//
// iframe {
// width: 100px;
// @include nb-ltr(margin-left, 1rem);
// @include nb-rtl(margin-right, 1rem);
// }
// }
//
// @include media-breakpoint-down(xl) {
// .control-item.search,
// .control-item.notifications,
// .control-item.email,
// .control-item.github-stars .text {
// display: none;
// }
//
// .control-item.github-stars {
// width: auto;
//
// iframe {
// margin: 0;
// }
// }
// }
//
// @include media-breakpoint-down(lg) {
// .downloads-count {
// display: none;
// }
// }
//
// @include media-breakpoint-down(md) {
// .theme-select {
// display: none;
// }
// }
//
// @include media-breakpoint-down(sm) {
// .contact-us {
// display: none;
// }
// }
//
// @include media-breakpoint-down(is) {
// .github-stars,
// .user-action {
// display: none;
// }
// }
//}
@include nb-install-component() {
img {
width: 100%;
object-fit: contain;
}
h4 {
text-align: center;
width: 100%;
height: 35px;
margin-bottom: 36px;
}
nb-layout-column {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
nb-card {
overflow: hidden;
cursor: pointer;
width: 32%;
}
nb-card-body {
padding: 0;
height: auto;
}
nb-layout-header {
display: flex;
justify-content: space-between;
width: 100%;
::ng-deep nav {
width: 100%;
justify-content: space-between;
}
.logo-container {
display: flex;
align-items: center;
width: calc(#{nb-theme(sidebar-width)} - #{nb-theme(header-padding)});
}
nb-action {
height: auto;
display: flex;
align-content: center;
}
.subtitle {
font-family: nb-theme(text-subtitle-font-family);
font-size: nb-theme(text-subtitle-font-size);
font-weight: nb-theme(text-subtitle-font-weight);
line-height: nb-theme(text-subtitle-line-height);
}
.downloads-count .number {
@include nb-ltr(margin-left, 0.5rem);
@include nb-rtl(margin-right, 0.5rem);
}
::ng-deep nb-search button {
padding: 0 !important;
}
.contact-us {
padding: 0;
nb-icon {
font-size: 1.25rem;
}
}
.header-container {
display: flex;
align-items: center;
width: auto;
.logo {
padding: 0 1.25rem;
font-size: 1.75rem;
margin-bottom: 0.5rem;
@include nb-rtl(border-right, 1px solid nb-theme(divider-color));
white-space: nowrap;
text-decoration: none;
}
}
.github-stars {
width: 245px;
display: flex;
iframe {
width: 100px;
@include nb-ltr(margin-left, 1rem);
@include nb-rtl(margin-right, 1rem);
}
}
@include media-breakpoint-down(xl) {
.control-item.github-stars .text {
display: none;
}
.control-item.github-stars {
width: auto;
iframe {
margin: 0;
}
}
}
@include media-breakpoint-down(lg) {
.downloads-count {
display: none;
}
}
@include media-breakpoint-down(md) {
}
@include media-breakpoint-down(sm) {
.contact-us {
display: none;
}
}
@include media-breakpoint-down(is) {
.github-stars{
display: none;
}
}
}
@include media-breakpoint-down(xl) {
h4 {
margin: 0;
}
}
@include media-breakpoint-down(lg) {
h4 {
margin-bottom: 36px;
}
nb-card-header {
padding: 12px 20px;
}
nb-card {
width: 48%;
}
}
@include media-breakpoint-down(md) {
}
@include media-breakpoint-down(sm) {
nb-card-header {
padding: 10px 20px;
}
}
@include media-breakpoint-down(is) {
nb-card {
width: 100%;
}
}
}

View file

@ -1,8 +1,77 @@
import {Component} from '@angular/core';
import {Component, enableProdMode, OnDestroy} from '@angular/core';
import {NbMediaBreakpoint, NbThemeService} from '@nebular/theme';
import {ActivatedRoute, Router} from '@angular/router';
import {AnalyticsService} from '../@core/utils';
import { environment } from '../../environments/environment';
@Component({
selector: 'ngx-starter',
templateUrl: './starter.component.html',
styleUrls: ['./starter.component.scss'],
})
export class NgxStarterComponent {}
export class NgxStarterComponent implements OnDestroy {
breakpoint: NbMediaBreakpoint;
breakpoints: any;
private alive = true;
themes = [
{
value: 'material-light',
name: 'Material Light',
},
{
value: 'dark',
name: 'Dark',
},
{
value: 'default',
name: 'Light',
},
{
value: 'material-dark',
name: 'Material Dark',
},
{
value: 'corporate',
name: 'Corporate',
},
{
value: 'cosmic',
name: 'Cosmic',
},
];
constructor(private router: Router,
private route: ActivatedRoute,
protected themeService: NbThemeService,
private analytics: AnalyticsService,
) {}
navigate(themeName: string) {
const currentTheme = {
themeName: themeName,
expires_in: this.calculateExpiration(environment.currentThemeLife),
};
localStorage.setItem('theme', JSON.stringify(currentTheme));
this.themeService.changeTheme(themeName);
this.router.navigate(['/pages/dashboard'], {queryParams: {theme: themeName}});
}
trackEmailClick() {
this.analytics.trackEvent('clickContactEmail', 'click');
}
ngOnDestroy() {
this.alive = false;
}
calculateExpiration(iat: number): number {
const currentDate = new Date().getTime();
const timestamp = iat || Math.floor(Date.now() / 1000);
return Math.floor(timestamp + currentDate);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 403 KiB

After

Width:  |  Height:  |  Size: 328 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 455 KiB

After

Width:  |  Height:  |  Size: 355 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 313 KiB

After

Width:  |  Height:  |  Size: 958 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 486 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 658 KiB

After

Width:  |  Height:  |  Size: 1.9 MiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 656 KiB

After

Width:  |  Height:  |  Size: 1.9 MiB

Before After
Before After

View file

@ -5,4 +5,5 @@
*/
export const environment = {
production: true,
currentThemeLife: 604800000, // 1 week in milliseconds
};

View file

@ -10,4 +10,5 @@
export const environment = {
production: false,
currentThemeLife: 604800000, // 1 week in milliseconds
};