feat: update npm packages

This commit is contained in:
Sergey Andrievskiy 2021-08-06 18:48:35 +03:00 committed by d.strigo
parent f6d9ec88ad
commit 7a22737611
321 changed files with 19716 additions and 84 deletions

View file

@ -54,6 +54,10 @@ import { VisitorsAnalyticsService } from './mock/visitors-analytics.service';
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';
import {MetadataService} from './utils/metadata.service';
const socialLinks = [
{
@ -93,7 +97,11 @@ const DATA_SERVICES = [
{ provide: StatsProgressBarData, useClass: StatsProgressBarService },
{ provide: VisitorsAnalyticsData, useClass: VisitorsAnalyticsService },
{ provide: SecurityCamerasData, useClass: SecurityCamerasService },
{provide: MAT_RIPPLE_GLOBAL_OPTIONS, useExisting: RippleService},
{ provide: MAT_RIPPLE_GLOBAL_OPTIONS, useExisting: RippleService },
];
const GUARDS = [
ThemeGuard,
];
export class NbSimpleRoleProvider extends NbRoleProvider {
@ -106,6 +114,7 @@ export class NbSimpleRoleProvider extends NbRoleProvider {
export const NB_CORE_PROVIDERS = [
...MockDataModule.forRoot().providers,
...DATA_SERVICES,
...GUARDS,
...NbAuthModule.forRoot({
strategies: [
@ -145,7 +154,10 @@ export const NB_CORE_PROVIDERS = [
LayoutService,
PlayerService,
SeoService,
MetadataService,
StateService,
AbService,
CurrentThemeService,
];
@NgModule({

View file

@ -0,0 +1,30 @@
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';
@Injectable()
export class ThemeGuard implements CanActivate {
constructor(private router: Router,
private currentThemeService: CurrentThemeService) {}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot,
): Observable<boolean> | Promise<boolean> | boolean {
return this.currentThemeService.currentTheme$.pipe(
map(theme => {
if (!theme || this.hasExpired(theme)) {
this.router.navigate(['themes']);
}
return true;
}));
}
private hasExpired(theme): boolean {
const currentThemeExpiration = JSON.parse(theme).expires_in;
const currentDate = new Date().getTime();
return currentDate > currentThemeExpiration;
}
}

View file

@ -0,0 +1,43 @@
import { Injectable } from '@angular/core';
import { Observable, BehaviorSubject, fromEvent as observableFromEvent } from 'rxjs';
import { filter } from 'rxjs/operators';
@Injectable()
export class AbService {
static readonly VARIANT_THEME_DEFAULT = 'theme-change-default';
static readonly VARIANT_THEME_COSMIC = 'theme-change-cosmic';
static readonly VARIANT_THEME_DARK = 'theme-change-dark';
static readonly VARIANT_THEME_CORPORATE = 'theme-change-corporate';
static readonly VARIANT_HIGHLIGHT_HIRE = 'highlight-hire';
static readonly VARIANT_DEVELOPERS_HIRE = 'developers-hire';
static readonly VARIANT_SOLUTION_HIRE = 'solution-hire';
static readonly VARIANT_HIDE_CALL_ACTION = 'call-action-hide';
// static readonly VARIANT_BANNER_HIRE = 'banner-hire';
private static readonly EVENT_NAME = 'ab-variant';
private static readonly AB_ENABLED = true;
private events$ = new BehaviorSubject<{ name: string }>(null);
constructor() {
if (AbService.AB_ENABLED) {
observableFromEvent<any>(document, AbService.EVENT_NAME)
.subscribe((e: { detail: any }) => {
if (e && e.detail) {
this.events$.next(e.detail);
}
});
}
}
onAbEvent(name: string = ''): Observable<{ name: string }> {
return this.events$.asObservable()
.pipe(
filter(e => !!(e && e.name)),
filter(e => name ? e.name === name : true),
);
}
}

View file

@ -1,16 +1,17 @@
import { Injectable } from '@angular/core';
import { Inject, Injectable } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router';
import { Location } from '@angular/common';
import { filter } from 'rxjs/operators';
declare const ga: any;
import { NB_WINDOW } from '@nebular/theme';
@Injectable()
export class AnalyticsService {
private enabled: boolean;
private enabled = false;
constructor(private location: Location, private router: Router) {
this.enabled = false;
constructor(@Inject(NB_WINDOW) private window,
private location: Location,
private router: Router) {
this.enabled = this.window.location.href.indexOf('akveo.com') >= 0;
}
trackPageViews() {
@ -19,14 +20,18 @@ export class AnalyticsService {
filter((event) => event instanceof NavigationEnd),
)
.subscribe(() => {
ga('send', {hitType: 'pageview', page: this.location.path()});
this.gtmPushToDataLayer({event: 'pageView' , path: this.location.path()});
});
}
}
trackEvent(eventName: string) {
trackEvent(eventName: string, eventVal: string = '') {
if (this.enabled) {
ga('send', 'event', eventName);
this.gtmPushToDataLayer({ event: eventName, eventValue: eventVal });
}
}
private gtmPushToDataLayer(params) {
this.window.dataLayer.push(params);
}
}

View file

@ -0,0 +1,26 @@
import {Injectable} from '@angular/core';
import {Meta, Title} from '@angular/platform-browser';
@Injectable()
export class MetadataService {
constructor(
private title: Title,
private meta: Meta,
) {}
updateTitle(title: string): void {
this.title.setTitle(title);
this.meta.updateTag({ property: 'og:title', content: title });
}
updateDescription(desc: string): void {
this.meta.updateTag({ name: 'description', content: desc });
this.meta.updateTag({ property: 'og:description', content: desc });
}
updateKeywords(keywords: string): void {
this.meta.updateTag({ name: 'keywords', content: keywords });
}
}

View file

@ -0,0 +1,37 @@
import {Injectable, OnDestroy} from '@angular/core';
import {Observable} from 'rxjs';
import {takeWhile} from 'rxjs/operators';
import {environment} from '../../../environments/environment';
@Injectable()
export class CurrentThemeService implements OnDestroy {
alive = true;
readonly currentTheme$: Observable<any> = new Observable(subscriber => {
subscriber.next(localStorage.theme);
}).pipe(takeWhile(() => this.alive));
setCurrentTheme(themeName: string): void {
const currentTheme = {
themeName: themeName,
expires_in: this.calculateExpiration(environment.currentThemeLife),
};
localStorage.setItem('theme', JSON.stringify(currentTheme));
}
getCurrentTheme(): string {
return localStorage.theme ? JSON.parse(localStorage.theme).themeName : 'default';
}
calculateExpiration(iat: number): number {
const currentDate = new Date().getTime();
const timestamp = iat || Math.floor(Date.now() / 1000);
return Math.floor(timestamp + currentDate);
}
ngOnDestroy(): void {
this.alive = false;
}
}

View file

@ -0,0 +1,64 @@
@import '../../../@theme/styles/themes';
@import '~bootstrap/scss/mixins/breakpoints';
@import '~@nebular/theme/styles/global/breakpoints';
@include nb-install-component() {
nb-card {
flex-direction: row;
align-items: center;
height: 6rem;
}
.icon-container {
height: 100%;
padding: 0.625rem;
}
.icon {
background-color: nb-theme(color-primary-default);
border-radius: nb-theme(card-border-radius);
display: flex;
align-items: center;
justify-content: center;
width: 5.75rem;
height: 100%;
transition: width 0.4s ease;
transform: translate3d(0, 0, 0);
-webkit-transform-style: preserve-3d;
-webkit-backface-visibility: hidden;
::ng-deep svg {
fill: nb-theme(text-control-color);
}
}
nb-icon {
font-size: 2.5rem;
}
.details {
flex: 1 1 auto;
display: flex;
flex-direction: column;
justify-content: center;
height: 100%;
@include nb-ltr(padding, 0 0.5rem 0 0.625rem);
@include nb-rtl(padding, 0 0.625rem 0 0.5rem);
border-left: 1px solid transparent;
}
.title {
margin: 0;
}
.actions {
@include nb-ltr(padding, 0 1.15rem 0 0.5rem);
@include nb-rtl(padding, 0 0.5rem 0 1.15rem);
}
@include media-breakpoint-down(md) {
.icon-container {
display: none;
}
}
}

View file

@ -0,0 +1,78 @@
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { NbComponentSize, NbComponentStatus, NbMediaBreakpointsService, NbThemeService } from '@nebular/theme';
import { map, takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';
@Component({
selector: 'ngx-call-action-card',
styleUrls: ['./call-action-card.component.scss'],
template: `
<nb-card>
<div class="icon-container">
<div class="icon">
<nb-icon [icon]="type"></nb-icon>
</div>
</div>
<div class="details">
<div class="title h6">{{ title }}</div>
</div>
<div class="actions">
<a nbButton [size]="buttonSize" [status]="getButtonStatus()" hero [href]="link">
{{ linkTitle }}
</a>
</div>
</nb-card>
`,
})
export class CallActionCardComponent implements OnInit, OnDestroy {
private destroy$ = new Subject<void>();
@Input() title: string;
@Input() type: string;
@Input() link: string;
@Input() linkTitle: string;
currentTheme: string;
buttonSize: NbComponentSize;
constructor(
private themeService: NbThemeService,
private breakpointService: NbMediaBreakpointsService,
) {}
ngOnInit() {
this.themeService.getJsTheme()
.pipe(takeUntil(this.destroy$))
.subscribe(theme => {
this.currentTheme = theme.name;
});
const { xxl } = this.breakpointService.getBreakpointsMap();
this.themeService.onMediaQueryChange()
.pipe(
map(([, currentBreakpoint]) => currentBreakpoint.width < xxl),
takeUntil(this.destroy$),
)
.subscribe((isLessThanXxl: boolean) => {
this.buttonSize = isLessThanXxl
? 'small'
: 'large';
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
getButtonStatus(): NbComponentStatus {
switch (this.currentTheme) {
case 'cosmic': return 'primary';
case 'corporate': return 'warning';
default: return 'danger';
}
}
}

View file

@ -5,13 +5,20 @@ import { Component } from '@angular/core';
styleUrls: ['./footer.component.scss'],
template: `
<span class="created-by">
Created with by <b><a href="https://akveo.page.link/8V2f" target="_blank">Akveo</a></b> 2019
Created with by <b><a href="https://www.akveo.com?utm_campaign=services%20-%20homepage%20-%20ngx_admin%20demo&utm_source=ngx_admin&utm_medium=referral&utm_content=demo_footer_link"
target="_blank">Akveo</a></b> 2019.
Made with
<b>
<a href="https://akveo.github.io/nebular/?utm_campaign=nebular%20-%20home%20-%20ngx_admin%20demo&utm_source=ngx_admin&utm_medium=referral&utm_content=nebular_footer_link" target="_blank">
Nebular.
</a>
</b>
</span>
<div class="socials">
<a href="#" target="_blank" class="ion ion-social-github"></a>
<a href="#" target="_blank" class="ion ion-social-facebook"></a>
<a href="#" target="_blank" class="ion ion-social-twitter"></a>
<a href="#" target="_blank" class="ion ion-social-linkedin"></a>
<a href="https://github.com/akveo/ngx-admin" target="_blank" class="ion ion-social-github"></a>
<a href="https://www.facebook.com/akveo/" target="_blank" class="ion ion-social-facebook"></a>
<a href="https://twitter.com/akveo_inc" target="_blank" class="ion ion-social-twitter"></a>
<a href="https://www.linkedin.com/company/akveo" target="_blank" class="ion ion-social-linkedin"></a>
</div>
`,
})

View file

@ -17,6 +17,7 @@
matRipple
[selected]="currentTheme"
(selectedChange)="changeTheme($event)"
class="theme-select"
>
<nb-option
*ngFor="let theme of themes"
@ -29,22 +30,53 @@
<div class="header-container">
<nb-actions size="small">
<nb-action class="control-item">
<nb-action class="control-item github-stars">
<span class="subtitle text"
[class.text-control]="materialTheme$ | async">
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"
[class.text-control]="materialTheme$ | async">
470.000
</span>
</nb-action>
<nb-action class="control-item contact-us"
matRipple
[matRippleUnbounded]="false"
[matRippleCentered]="true">
<a nbButton ghost href="mailto:contact@akveo.com" (click)="trackEmailClick()">
<nb-icon icon="email-outline" pack="eva"></nb-icon>
<span [class.text-control]="materialTheme$ | async">
contact@akveo.com
</span>
</a>
</nb-action>
<nb-action class="control-item search">
<nb-search
type="rotate-layout"
matRipple
[matRippleUnbounded]="true"
[matRippleCentered]="true"
(click)="startSearch()"
></nb-search></nb-action>
<nb-action
class="control-item"
class="control-item email"
icon="email-outline"
matRipple
[matRippleUnbounded]="true"
[matRippleCentered]="true"
></nb-action>
<nb-action
class="control-item"
class="control-item notifications"
icon="bell-outline"
matRipple
[matRippleUnbounded]="true"
@ -54,7 +86,7 @@
class="user-action"
*nbIsGranted="['view', 'user']"
matRipple
[matRippleUnbounded]="true"
[matRippleUnbounded]="false"
[matRippleCentered]="true">
<nb-user [nbContextMenu]="userMenu"
[onlyPicture]="userPictureOnly"

View file

@ -23,10 +23,30 @@
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;
@ -52,18 +72,55 @@
}
}
@include media-breakpoint-down(sm) {
.control-item {
.github-stars {
width: 250px;
display: flex;
iframe {
width: 105px;
@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;
}
.user-action {
border: none;
padding: 0;
.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) {
nb-select {
.github-stars,
.user-action {
display: none;
}
}

View file

@ -2,10 +2,11 @@ import { Component, OnDestroy, OnInit } from '@angular/core';
import { NbMediaBreakpointsService, NbMenuService, NbSidebarService, NbThemeService } from '@nebular/theme';
import { UserData } from '../../../@core/data/users';
import { LayoutService } from '../../../@core/utils';
import { AnalyticsService, LayoutService } from '../../../@core/utils';
import { map, takeUntil } from 'rxjs/operators';
import { Subject, Observable } from 'rxjs';
import { RippleService } from '../../../@core/utils/ripple.service';
import {CurrentThemeService} from '../../../@core/utils/theme.service';
@Component({
selector: 'ngx-header',
@ -15,7 +16,7 @@ import { RippleService } from '../../../@core/utils/ripple.service';
export class HeaderComponent implements OnInit, OnDestroy {
private destroy$: Subject<void> = new Subject<void>();
public readonly materialTheme$: Observable<boolean>;
public materialTheme$: Observable<boolean>;
userPictureOnly: boolean = false;
user: any;
@ -58,12 +59,14 @@ export class HeaderComponent implements OnInit, OnDestroy {
private layoutService: LayoutService,
private breakpointService: NbMediaBreakpointsService,
private rippleService: RippleService,
private analytics: AnalyticsService,
private currentThemeService: CurrentThemeService,
) {
this.materialTheme$ = this.themeService.onThemeChange()
.pipe(map(theme => {
const themeName: string = theme?.name || '';
return themeName.startsWith('material');
}));
this.materialTheme$ = new Observable(subscriber => {
const themeName: string = this.currentThemeService.getCurrentTheme();
subscriber.next(themeName.startsWith('material'));
});
}
ngOnInit() {
@ -98,7 +101,12 @@ export class HeaderComponent implements OnInit, OnDestroy {
}
changeTheme(themeName: string) {
this.currentThemeService.setCurrentTheme(themeName);
this.themeService.changeTheme(themeName);
this.materialTheme$ = new Observable(subscriber => {
subscriber.next(this.currentThemeService.getCurrentTheme().startsWith('material'));
});
}
toggleSidebar(): boolean {
@ -112,4 +120,12 @@ export class HeaderComponent implements OnInit, OnDestroy {
this.menuService.navigateHome();
return false;
}
startSearch() {
this.analytics.trackEvent('startSearch');
}
trackEmailClick() {
this.analytics.trackEvent('clickContactEmail', 'click');
}
}

View file

@ -2,3 +2,8 @@ export * from './header/header.component';
export * from './footer/footer.component';
export * from './search-input/search-input.component';
export * from './tiny-mce/tiny-mce.component';
export * from './call-action-card/call-action-card.component';
export * from './theme-settings/theme-settings.component';
export * from './switcher/switcher.component';
export * from './layout-direction-switcher/layout-direction-switcher.component';
export * from './toggle-settings-button/toggle-settings-button.component';

View file

@ -0,0 +1,45 @@
import { Component, OnDestroy, Input } from '@angular/core';
import { NbLayoutDirectionService, NbLayoutDirection } from '@nebular/theme';
import { takeWhile } from 'rxjs/operators';
import { AnalyticsService } from '../../../@core/utils/analytics.service';
@Component({
selector: 'ngx-layout-direction-switcher',
template: `
<ngx-switcher
[firstValue]="directions.RTL"
[secondValue]="directions.LTR"
[firstValueLabel]="'RTL'"
[secondValueLabel]="'LTR'"
[value]="currentDirection"
(valueChange)="toggleDirection($event)"
[vertical]="vertical">
</ngx-switcher>
`,
})
export class LayoutDirectionSwitcherComponent implements OnDestroy {
directions = NbLayoutDirection;
currentDirection: NbLayoutDirection;
alive = true;
@Input() vertical: boolean = false;
constructor(private directionService: NbLayoutDirectionService,
private analyticsService: AnalyticsService) {
this.currentDirection = this.directionService.getDirection();
this.directionService.onDirectionChange()
.pipe(takeWhile(() => this.alive))
.subscribe(newDirection => this.currentDirection = newDirection);
}
toggleDirection(newDirection) {
this.directionService.setDirection(newDirection);
this.analyticsService.trackEvent('toggleDirection', newDirection);
}
ngOnDestroy() {
this.alive = false;
}
}

View file

@ -0,0 +1,91 @@
@import '../../styles/themes';
@import '~bootstrap/scss/mixins/breakpoints';
@import '~@nebular/theme/styles/global/breakpoints';
@include nb-install-component() {
.switch-label {
display: flex;
justify-content: space-around;
align-items: center;
cursor: pointer;
margin: 0;
&.vertical {
flex-direction: column;
align-items: flex-start;
.first,
.second {
padding: 0;
}
.switch {
margin-top: 0.5em;
}
}
& > span {
transition: opacity 0.3s ease;
color: nb-theme(text-hint-color);
&.first {
@include nb-ltr(padding-right, 10px);
@include nb-rtl(padding-left, 10px);
}
&.second {
@include nb-ltr(padding-left, 10px);
@include nb-rtl(padding-right, 10px);
}
&.active {
color: nb-theme(text-basic-color);
}
&:active {
opacity: 0.78;
}
}
}
.switch {
position: relative;
display: inline-block;
width: 3rem;
height: 1.5rem;
margin: 0;
input {
display: none;
&:checked + .slider::before {
@include nb-ltr(transform, translateX(1.5rem));
@include nb-rtl(transform, translateX(-1.5rem));
}
}
.slider {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: 1.75rem;
background-color: nb-theme(background-basic-color-2);
}
.slider::before {
position: absolute;
content: '';
height: 1.5rem;
width: 1.5rem;
border-radius: 50%;
background-color: nb-theme(color-primary-default);
transition: 0.2s;
}
}
@include media-breakpoint-down(xs) {
align-items: flex-end;
}
}

View file

@ -0,0 +1,58 @@
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'ngx-switcher',
styleUrls: ['./switcher.component.scss'],
template: `
<label class="switch-label" [class.vertical]="vertical">
<span class="first" [class.active]="vertical || isFirstValue()">
{{vertical ? currentValueLabel() : firstValueLabel}}
</span>
<div class="switch">
<input type="checkbox" [checked]="isSecondValue()" (change)="changeValue()">
<span class="slider"></span>
</div>
<span *ngIf="!vertical"
class="second"
[class.active]="isSecondValue()">
{{secondValueLabel}}
</span>
</label>
`,
})
export class SwitcherComponent {
@Input() firstValue: any;
@Input() secondValue: any;
@Input() firstValueLabel: string;
@Input() secondValueLabel: string;
@Input() vertical: boolean;
@Input() value: any;
@Output() valueChange = new EventEmitter<any>();
isFirstValue() {
return this.value === this.firstValue;
}
isSecondValue() {
return this.value === this.secondValue;
}
currentValueLabel() {
return this.isFirstValue()
? this.firstValueLabel
: this.secondValueLabel;
}
changeValue() {
this.value = this.isFirstValue()
? this.secondValue
: this.firstValue;
this.valueChange.emit(this.value);
}
}

View file

@ -0,0 +1,35 @@
@import '../../styles/themes';
@include nb-install-component() {
.subheader {
display: block;
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);
margin-bottom: 0.875rem;
text-align: center;
}
.layout-setting-heading {
margin-bottom: 1.2rem;
}
.setting-icon {
font-size: 1.5rem;
}
.settings-row {
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
flex-wrap: wrap;
width: 100%;
margin: 0 0 2.575rem;
}
.switcher {
width: 12rem;
}
}

View file

@ -0,0 +1,75 @@
import { Component } from '@angular/core';
import { StateService } from '../../../@core/utils';
@Component({
selector: 'ngx-theme-settings',
styleUrls: ['./theme-settings.component.scss'],
template: `
<span class="subheader">Layouts</span>
<div class="settings-row">
<button *ngFor="let layout of layouts"
nbButton
[appearance]="layout.selected ? 'outline' : 'ghost'"
[attr.aria-label]="layout.name"
(click)="layoutSelect(layout)"
class="select-button">
<i [attr.class]="layout.icon + ' setting-icon'"></i>
</button>
</div>
<span class="subheader">Sidebar</span>
<div class="settings-row">
<button *ngFor="let sidebar of sidebars"
nbButton
[appearance]="sidebar.selected ? 'outline' : 'ghost'"
[attr.aria-label]="sidebar.name"
(click)="sidebarSelect(sidebar)"
class="select-button">
<i [attr.class]="sidebar.icon + ' setting-icon'"></i>
</button>
</div>
<span class="subheader layout-setting-heading">Layout direction</span>
<div class="settings-row">
<div class="switcher">
<ngx-layout-direction-switcher></ngx-layout-direction-switcher>
</div>
</div>
`,
})
export class ThemeSettingsComponent {
layouts = [];
sidebars = [];
constructor(protected stateService: StateService) {
this.stateService.getLayoutStates()
.subscribe((layouts: any[]) => this.layouts = layouts);
this.stateService.getSidebarStates()
.subscribe((sidebars: any[]) => this.sidebars = sidebars);
}
layoutSelect(layout: any): boolean {
this.layouts = this.layouts.map((l: any) => {
l.selected = false;
return l;
});
layout.selected = true;
this.stateService.setLayoutState(layout);
return false;
}
sidebarSelect(sidebar: any): boolean {
this.sidebars = this.sidebars.map((s: any) => {
s.selected = false;
return s;
});
sidebar.selected = true;
this.stateService.setSidebarState(sidebar);
return false;
}
}

View file

@ -0,0 +1,112 @@
@import '../../styles/themes';
@import '~bootstrap/scss/mixins/breakpoints';
@import '~@nebular/theme/styles/global/breakpoints';
@include nb-install-component() {
border-radius: nb-theme(button-rectangle-border-radius);
box-shadow: nb-theme(card-shadow);
position: fixed;
top: 50%;
transition: transform 0.3s ease;
z-index: 998;
@include nb-ltr() {
&.position-start {
left: 0;
&,
.toggle-settings {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
&.expanded {
transform: translateX(nb-theme(sidebar-width));
}
}
&.position-end {
right: 0;
&,
.toggle-settings {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
&.expanded {
transform: translateX(-#{nb-theme(sidebar-width)});
}
}
}
@include nb-rtl() {
&.position-start {
right: 0;
&,
.toggle-settings {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
&.expanded {
transform: translateX(-#{nb-theme(sidebar-width)});
}
}
&.position-end {
left: 0;
&,
.toggle-settings {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
&.expanded {
transform: translateX(nb-theme(sidebar-width));
}
}
}
.toggle-settings {
background: nb-theme(color-basic-100);
box-shadow: none;
border: none;
height: 3rem;
width: 3rem;
padding: 0;
text-align: center;
}
.icon {
font-size: 1.65rem;
&.icon-pulse {
animation-name: gear-pulse;
animation-duration: 1s;
animation-iteration-count: infinite;
}
}
@keyframes gear-pulse {
from {
transform: scale3d(1, 1, 1);
}
50% {
transform: scale3d(1.2, 1.2, 1.2);
}
to {
transform: scale3d(1, 1, 1);
}
}
@include media-breakpoint-down(sm) {
.toggle-settings {
display: none;
}
}
}

View file

@ -0,0 +1,53 @@
import { Component, HostBinding, OnDestroy, OnInit } from '@angular/core';
import { NbSidebarService } from '@nebular/theme';
import { StateService } from '../../../@core/utils';
import { Subject } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators';
@Component({
selector: 'ngx-toggle-settings-button',
styleUrls: ['./toggle-settings-button.component.scss'],
template: `
<button nbButton appearance="outline" class="toggle-settings" (click)="toggleSettings()">
<nb-icon class="icon" [class.icon-pulse]="enablePulse" icon="settings-2-outline" pack="eva"></nb-icon>
</button>
`,
})
export class ToggleSettingsButtonComponent implements OnInit, OnDestroy {
protected destroy$ = new Subject<void>();
enablePulse = true;
@HostBinding('class.position-start') positionStart = false;
@HostBinding('class.position-end') positionEnd = false;
@HostBinding('class.expanded') expanded = false;
constructor(
protected sidebarService: NbSidebarService,
protected stateService: StateService,
) {}
ngOnInit() {
this.stateService.onSidebarState()
.pipe(
map(sidebar => sidebar.id !== 'end'),
takeUntil(this.destroy$),
)
.subscribe((isSettingsSidebarEnd: boolean) => {
this.positionEnd = isSettingsSidebarEnd;
this.positionStart = !isSettingsSidebarEnd;
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
toggleSettings() {
this.sidebarService.toggle(false, 'settings-sidebar');
this.expanded = !this.expanded;
this.enablePulse = false;
}
}

View file

@ -1,3 +1,4 @@
export * from './one-column/one-column.layout';
export * from './two-columns/two-columns.layout';
export * from './three-columns/three-columns.layout';
export * from './sample/sample.layout';

View file

@ -0,0 +1,25 @@
@import '../../styles/themes';
@import '~bootstrap/scss/mixins/breakpoints';
@import '~@nebular/theme/styles/global/breakpoints';
@include nb-install-component() {
nb-layout-column.small {
flex: 0.15 !important;
}
nb-sidebar.settings-sidebar {
$sidebar-width: 19rem;
transition: transform 0.3s ease;
@include nb-ltr(transform, translate3d(100%, 0, 0));
@include nb-rtl(transform, translate3d(-100%, 0, 0));
&.start {
@include nb-ltr(transform, translate3d(-100%, 0, 0));
@include nb-rtl(transform, translate3d(100%, 0, 0));
}
&.expanded, &.expanded.start {
transform: translate3d(0, 0, 0);
}
}
}

View file

@ -0,0 +1,159 @@
import { Component, Inject, OnDestroy, OnInit, PLATFORM_ID, ViewChild } from '@angular/core';
import { Subject } from 'rxjs';
import { delay, withLatestFrom, takeUntil } from 'rxjs/operators';
import {
NbLayoutComponent,
NbMediaBreakpoint,
NbMediaBreakpointsService,
NbMenuItem,
NbMenuService,
NbSidebarService,
NbThemeService,
} from '@nebular/theme';
import { StateService } from '../../../@core/utils';
@Component({
selector: 'ngx-sample-layout',
styleUrls: ['./sample.layout.scss'],
template: `
<nb-layout [center]="layout.id === 'center-column'" windowMode>
<nb-layout-header fixed>
<ngx-header></ngx-header>
<ngx-toggle-settings-button></ngx-toggle-settings-button>
</nb-layout-header>
<nb-sidebar class="menu-sidebar"
tag="menu-sidebar"
responsive
[end]="isMenuSidebarPositionEnd()">
<ng-content select="nb-menu"></ng-content>
</nb-sidebar>
<nb-layout-column class="main-content">
<ng-content select="router-outlet"></ng-content>
</nb-layout-column>
<nb-layout-column start class="small" *ngIf="layout.id === 'two-column' || layout.id === 'three-column'">
<nb-menu [items]="subMenu"></nb-menu>
</nb-layout-column>
<nb-layout-column class="small" *ngIf="layout.id === 'three-column'">
<nb-menu [items]="subMenu"></nb-menu>
</nb-layout-column>
<nb-layout-footer fixed>
<ngx-footer></ngx-footer>
</nb-layout-footer>
<nb-sidebar class="settings-sidebar"
tag="settings-sidebar"
state="collapsed"
fixed
[end]="isSettingsSidebarPositionEnd()">
<ngx-theme-settings></ngx-theme-settings>
</nb-sidebar>
</nb-layout>
`,
})
export class SampleLayoutComponent implements OnInit, OnDestroy {
protected destroy$ = new Subject<void>();
subMenu: NbMenuItem[] = [
{
title: 'PAGE LEVEL MENU',
group: true,
},
{
title: 'Buttons',
icon: 'ion ion-android-radio-button-off',
link: '/pages/ui-features/buttons',
},
{
title: 'Grid',
icon: 'ion ion-android-radio-button-off',
link: '/pages/ui-features/grid',
},
{
title: 'Icons',
icon: 'ion ion-android-radio-button-off',
link: '/pages/ui-features/icons',
},
{
title: 'Modals',
icon: 'ion ion-android-radio-button-off',
link: '/pages/ui-features/modals',
},
{
title: 'Typography',
icon: 'ion ion-android-radio-button-off',
link: '/pages/ui-features/typography',
},
{
title: 'Animated Searches',
icon: 'ion ion-android-radio-button-off',
link: '/pages/ui-features/search-fields',
},
{
title: 'Tabs',
icon: 'ion ion-android-radio-button-off',
link: '/pages/ui-features/tabs',
},
];
layout: any = {};
sidebar: any = {};
currentTheme: string;
@ViewChild(NbLayoutComponent, { static: false }) layoutComponent: NbLayoutComponent;
constructor(protected stateService: StateService,
protected menuService: NbMenuService,
protected themeService: NbThemeService,
protected bpService: NbMediaBreakpointsService,
protected sidebarService: NbSidebarService,
@Inject(PLATFORM_ID) protected platformId,
) {}
ngOnInit() {
this.stateService.onLayoutState()
.pipe(takeUntil(this.destroy$))
.subscribe(layout => this.layout = layout);
this.stateService.onSidebarState()
.pipe(takeUntil(this.destroy$))
.subscribe(sidebar => this.sidebar = sidebar);
const isBp = this.bpService.getByName('is');
this.menuService.onItemSelect()
.pipe(
withLatestFrom(this.themeService.onMediaQueryChange()),
delay(20),
takeUntil(this.destroy$),
)
.subscribe(([item, [bpFrom, bpTo]]: [any, [NbMediaBreakpoint, NbMediaBreakpoint]]) => {
if (bpTo.width <= isBp.width) {
this.sidebarService.collapse('menu-sidebar');
}
});
this.themeService.getJsTheme()
.pipe(takeUntil(this.destroy$))
.subscribe(theme => this.currentTheme = theme.name);
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
isMenuSidebarPositionEnd(): boolean {
return this.sidebar.id === 'end';
}
isSettingsSidebarPositionEnd(): boolean {
return !this.isMenuSidebarPositionEnd();
}
}

View file

@ -13,6 +13,7 @@ import {
NbSelectModule,
NbIconModule,
NbThemeModule,
NbCardModule,
} from '@nebular/theme';
import { NbEvaIconsModule } from '@nebular/eva-icons';
import { NbSecurityModule } from '@nebular/security';
@ -22,6 +23,11 @@ import {
HeaderComponent,
SearchInputComponent,
TinyMCEComponent,
CallActionCardComponent,
ToggleSettingsButtonComponent,
LayoutDirectionSwitcherComponent,
SwitcherComponent,
ThemeSettingsComponent,
} from './components';
import {
CapitalizePipe,
@ -34,6 +40,7 @@ import {
OneColumnLayoutComponent,
ThreeColumnsLayoutComponent,
TwoColumnsLayoutComponent,
SampleLayoutComponent,
} from './layouts';
import { DEFAULT_THEME } from './styles/theme.default';
import { COSMIC_THEME } from './styles/theme.cosmic';
@ -55,6 +62,7 @@ const NB_MODULES = [
NbSelectModule,
NbIconModule,
NbEvaIconsModule,
NbCardModule,
];
const COMPONENTS = [
HeaderComponent,
@ -64,6 +72,12 @@ const COMPONENTS = [
OneColumnLayoutComponent,
ThreeColumnsLayoutComponent,
TwoColumnsLayoutComponent,
CallActionCardComponent,
ToggleSettingsButtonComponent,
LayoutDirectionSwitcherComponent,
SwitcherComponent,
SampleLayoutComponent,
ThemeSettingsComponent,
];
const PIPES = [
CapitalizePipe,

View file

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

View file

@ -4,8 +4,14 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { withLatestFrom, filter } from 'rxjs/operators';
import { NbThemeService } from '@nebular/theme';
import { AnalyticsService } from './@core/utils/analytics.service';
import { AbService } from './@core/utils/ab.service';
import { SeoService } from './@core/utils/seo.service';
import { CurrentThemeService } from './@core/utils/theme.service';
@Component({
selector: 'ngx-app',
@ -13,11 +19,52 @@ import { SeoService } from './@core/utils/seo.service';
})
export class AppComponent implements OnInit {
constructor(private analytics: AnalyticsService, private seoService: SeoService) {
themes = ['default', 'cosmic', 'corporate', 'dark', 'material-dark', 'material-light'];
constructor(private analytics: AnalyticsService,
private seoService: SeoService,
private activatedRoute: ActivatedRoute,
private abService: AbService,
private themeService: NbThemeService,
private currentThemeService: CurrentThemeService) {
this.themeService.onThemeChange()
.subscribe((theme: any) => {
this.analytics.trackEvent('changeTheme', theme.name);
});
this.activatedRoute.queryParams
.subscribe((params: any) => {
if (params.theme && this.themes.includes(params.theme)) {
this.themeService.changeTheme(params.theme);
this.currentThemeService.setCurrentTheme(params.theme);
} else {
this.themeService.changeTheme(this.currentThemeService.getCurrentTheme());
}
});
}
ngOnInit(): void {
const variants = [
AbService.VARIANT_THEME_CORPORATE,
AbService.VARIANT_THEME_DEFAULT,
AbService.VARIANT_THEME_COSMIC,
AbService.VARIANT_THEME_DARK,
];
this.analytics.trackPageViews();
this.seoService.trackCanonicalChanges();
this.abService.onAbEvent()
.pipe(
withLatestFrom(this.activatedRoute.queryParams),
filter(([e, params]: [{ name: string }, any]) => !params.theme),
)
.subscribe(([e, params]: [{ name: string }, any]) => {
const themeName = e.name.replace('theme-change-', '');
if (variants.includes(e.name) && this.themes.includes(themeName)) {
this.themeService.changeTheme(themeName);
}
});
}
}

View file

@ -20,6 +20,7 @@ import {
NbToastrModule,
NbWindowModule,
} from '@nebular/theme';
import {InlineSVGModule} from 'ng-inline-svg';
@NgModule({
declarations: [AppComponent],
@ -39,6 +40,7 @@ import {
}),
CoreModule.forRoot(),
ThemeModule.forRoot(),
InlineSVGModule.forRoot(),
],
bootstrap: [AppComponent],
})

View file

@ -0,0 +1,42 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { BackendIntegrationComponent } from './backend-integration.component';
import { PhpIntegrationDescriptionComponent } from './descriptions/php-integration-description.component';
import { DotNetCoreIntegrationDescriptionComponent } from './descriptions/dot-net-core-integration-description.component';
import { NodeJsIntegrationDescriptionComponent } from './descriptions/node-js-integration-description.component';
import { JavaIntegrationDescriptionComponent } from './descriptions/java-integration-description.component';
import { EcommerceIntegrationDescriptionComponent } from './descriptions/ecommerce-integration-description.component';
const routes: Routes = [{
path: '',
component: BackendIntegrationComponent,
children: [
{
path: 'php',
component: PhpIntegrationDescriptionComponent,
},
{
path: 'dot-net-core',
component: DotNetCoreIntegrationDescriptionComponent,
},
{
path: 'node-js',
component: NodeJsIntegrationDescriptionComponent,
},
{
path: 'java',
component: JavaIntegrationDescriptionComponent,
},
{
path: 'ecommerce',
component: EcommerceIntegrationDescriptionComponent,
},
],
}];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class BackendIntegrationRoutingModule { }

View file

@ -0,0 +1,58 @@
@import '../../@theme/styles/themes';
@import '~@nebular/theme/styles/global/breakpoints';
:host {
display: flex !important;
flex-direction: row;
justify-content: stretch;
align-items: stretch;
height: 100%;
}
.description-container, .diagram-container {
display: flex;
}
.diagram-container {
flex: 2 1 auto;
align-items: center;
min-width: 30rem;
width: 70%;
}
.description-container {
flex: 1 2 auto;
max-width: 45rem;
align-items: stretch;
height: 100%;
::ng-deep :last-child {
width: 100%;
}
}
@include media-breakpoint-down(lg) {
:host {
flex-direction: column;
justify-content: flex-start;
align-items: center;
.diagram-container, .description-container {
width: 100%;
max-width: none;
}
.description-container {
padding-bottom: 1.5rem;
::ng-deep ngx-integration-description .subheader {
margin-bottom: 1.5rem;
}
}
.diagram-container {
min-width: 0;
}
}
}

View file

@ -0,0 +1,16 @@
import {Component} from '@angular/core';
@Component({
selector: 'ngx-backend-integration',
template: `
<div class="diagram-container">
<ngx-backend-integration-diagram></ngx-backend-integration-diagram>
</div>
<div class="description-container">
<router-outlet></router-outlet>
</div>
`,
styleUrls: ['./backend-integration.component.scss'],
})
export class BackendIntegrationComponent {
}

View file

@ -0,0 +1,38 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { NbButtonModule, NbCardModule, NbIconModule } from '@nebular/theme';
import { InlineSVGModule } from 'ng-inline-svg';
import { BackendIntegrationDiagramComponent } from './diagram/backend-integration-diagram.component';
import { BackendIntegrationComponent } from './backend-integration.component';
import { BackendIntegrationRoutingModule } from './backend-integration-routing.module';
import { IntegrationDescriptionComponent } from './integration-description/integration-description.component';
import { PhpIntegrationDescriptionComponent } from './descriptions/php-integration-description.component';
import {
DotNetCoreIntegrationDescriptionComponent,
} from './descriptions/dot-net-core-integration-description.component';
import { NodeJsIntegrationDescriptionComponent } from './descriptions/node-js-integration-description.component';
import { JavaIntegrationDescriptionComponent } from './descriptions/java-integration-description.component';
import { EcommerceIntegrationDescriptionComponent } from './descriptions/ecommerce-integration-description.component';
@NgModule({
imports: [
RouterModule,
NbCardModule,
NbIconModule,
NbButtonModule,
InlineSVGModule,
BackendIntegrationRoutingModule,
],
declarations: [
BackendIntegrationComponent,
BackendIntegrationDiagramComponent,
PhpIntegrationDescriptionComponent,
DotNetCoreIntegrationDescriptionComponent,
NodeJsIntegrationDescriptionComponent,
JavaIntegrationDescriptionComponent,
EcommerceIntegrationDescriptionComponent,
IntegrationDescriptionComponent,
],
})
export class BackendIntegrationModule { }

View file

@ -0,0 +1,31 @@
import { Component } from '@angular/core';
@Component({
selector: 'ngx-dot-net-core-integration-description',
template: `
<ngx-integration-description [features]="features"
[url]="url"
[buttonText]="buttonText"
></ngx-integration-description>
`,
})
export class DotNetCoreIntegrationDescriptionComponent {
buttonText = 'Get Backend From 49$';
url = 'https://store.akveo.com/collections/net-core-bundles?utm_campaign=akveo_store%20-%20all%20bundles%20-%20ngx_admin_demo&utm_source=ngx_admin&utm_medium=referral&utm_content=sidebar_link_dotnetcore';
features: string[] = [
'Backend layered architecture, authentication, solution structure',
'Ngx-admin template with 100+ UI components',
'Authentication using JWT tokens is implemented and integrated into both client and server side',
'Basic role management and ACL is in place',
'Data entities classes, independent of any ORM',
'Dependency injection takes care of services and repositories instantiation',
'Swagger included for automatic API testing and documentation',
'Serilog is used for logging',
'OWIN startup is configured',
'Documentation is included',
'3 months free updates',
];
}

View file

@ -0,0 +1,22 @@
import { Component } from '@angular/core';
@Component({
selector: 'ngx-e-commerce-integration-description',
template: `
<ngx-integration-description [features]="features"
[url]="url"
[buttonText]="buttonText"
></ngx-integration-description>
`,
})
export class EcommerceIntegrationDescriptionComponent {
buttonText = 'Get Backend From 49$';
url = 'https://store.akveo.com/collections/e-commerce-bundles?utm_campaign=akveo_store%20-%20all%20bundles%20-%20ngx_admin_demo&utm_source=ngx_admin&utm_medium=referral&utm_content=sidebar_link_e-commerce';
features: string[] = [
'E-commerce dashboard components integrated with backend',
'Sample order table and order details page integrated with backend',
];
}

View file

@ -0,0 +1,31 @@
import { Component } from '@angular/core';
@Component({
selector: 'ngx-java-integration-description',
template: `
<ngx-integration-description [features]="features"
[url]="url"
[buttonText]="buttonText"
></ngx-integration-description>
`,
})
export class JavaIntegrationDescriptionComponent {
buttonText = 'Get Backend From 49$';
url = 'https://store.akveo.com/collections/java-bundles?utm_campaign=akveo_store%20-%20all%20bundles%20-%20ngx_admin_demo&utm_source=ngx_admin&utm_medium=referral&utm_content=sidebar_link_java';
features: string[] = [
'Ngx-admin template with 100+ UI components',
'Spring Boot as the main framework for backend',
'Maven as building tool',
'Can be used with a range of SQL databases. In-Memory database H2 by default',
'Authentication using Json Web Tokens is implemented and integrated with both client and server side',
'Refresh Token functionality is available out of the box',
`TSLint as part of Angular project settings, it simply wouldn't let you push typescript code with errors`,
'Backend has Checkstyle setup and findbugs plugin for static code analysis',
'Swagger for API documentation purpose',
'Documentation is included',
'3 months free updates',
];
}

View file

@ -0,0 +1,31 @@
import { Component } from '@angular/core';
@Component({
selector: 'ngx-node-js-integration-description',
template: `
<ngx-integration-description [features]="features"
[url]="url"
[buttonText]="buttonText"
></ngx-integration-description>
`,
})
export class NodeJsIntegrationDescriptionComponent {
buttonText = 'Get Backend From 49$';
url = 'https://store.akveo.com/collections/nodejs-bundles?utm_campaign=akveo_store%20-%20all%20bundles%20-%20ngx_admin_demo&utm_source=ngx_admin&utm_medium=referral&utm_content=sidebar_link_nodejs';
features: string[] = [
'MongoDB for user data storage',
'Express server',
'Authentication using Passport and JWT tokens is implemented and integrated with both client and server side',
'Eslint for code quality on the backend side',
'Winston is used for logging',
'Node-config is used for API settings',
'Nodemon is used for better development experience',
'Documentation is included',
'Basic role management and ACL is in place',
'Swagger included for automatic API testing and documentation',
'3 months free updates',
];
}

View file

@ -0,0 +1,29 @@
import { Component } from '@angular/core';
@Component({
selector: 'ngx-php-integration-description',
template: `
<ngx-integration-description [features]="features"
[url]="url"
[buttonText]="buttonText"
></ngx-integration-description>
`,
})
export class PhpIntegrationDescriptionComponent {
buttonText = 'Get Backend From 49$';
url = 'https://store.akveo.com/products/material-php-starter-bundle?utm_campaign=akveo_store%20-%20all%20bundles%20-%20ngx_admin_demo&utm_source=ngx_admin%20&utm_medium=referral%20&utm_content=sidebar_link_php';
features: string[] = [
'Ngx-admin template with 100+ UI Nebular and Eva design components',
'Authentication using JWT tokens is implemented and integrated into both client and server-side',
'Basic role management and ACL is in place, AUTH, reset the password',
'Backend solution layered architecture and projects segregation',
'Swagger included for automatic API testing and documentation',
'Documentation is included',
'Docker and docker-compose configuration included',
'MySQL database',
'3 months free updates',
];
}

View file

@ -0,0 +1,2 @@
<div inlineSVG="assets/images/backend-integration-diagram.svg">
</div>

View file

@ -0,0 +1,29 @@
@import '../../../@theme/styles/themes';
@include nb-install-component() {
width: 100%;
::ng-deep svg {
path.card-header, path.subcard-header, path.diagram-header {
fill: nb-theme(text-basic-color);
}
path.card-subheader, path.subcard-subheader, path.diagram-subheader {
fill: nb-theme(text-hint-color);
}
rect.card-background {
fill: nb-theme(background-basic-color-2);
stroke: nb-theme(border-basic-color-5);
}
rect.subcard-background {
fill: nb-theme(background-basic-color-1);
}
path.subcard-border {
fill: nb-theme(background-basic-color-2);
stroke: nb-theme(border-basic-color-5);
}
}
}

View file

@ -0,0 +1,8 @@
import { Component } from '@angular/core';
@Component({
selector: 'ngx-backend-integration-diagram',
templateUrl: './backend-integration-diagram.component.html',
styleUrls: ['./backend-integration-diagram.component.scss'],
})
export class BackendIntegrationDiagramComponent {}

View file

@ -0,0 +1,30 @@
@import '../../../@theme/styles/themes';
@include nb-install-component() {
background-color: nb-theme(background-basic-color-1);
display: flex;
flex-direction: column;
justify-content: center;
padding: 1rem 2rem;
width: 100%;
height: 100%;
.h6 {
margin-bottom: 1rem;
}
.subtitle {
margin-bottom: 3rem;
}
.features-list {
list-style-type: '- ';
padding-left: 0;
}
a {
width: auto;
align-self: flex-start;
margin-top: 2rem;
}
}

View file

@ -0,0 +1,30 @@
import {Component, Input} from '@angular/core';
@Component({
selector: 'ngx-integration-description',
template: `
<div class="h6"> For why do you need a backend admin dashboard?</div>
<div class="subtitle"> To save up to 300 hours on development. To use backend as ready to use examples. </div>
<div class="h6"> Features </div>
<ul class="features-list">
<li *ngFor="let feature of features"
class="feature"
>
{{ feature }}
</li>
</ul>
<a nbButton
[href]="url"
status="primary"
target="_blank"
> {{buttonText}} </a>
`,
styleUrls: ['./integration-description.component.scss'],
})
export class IntegrationDescriptionComponent {
@Input() features: string[];
@Input() url: string;
@Input() buttonText: string;
}

View file

@ -1,3 +1,19 @@
<div class="row" *ngIf="showCallAction">
<div class="col-12 col-md-6">
<ngx-call-action-card title="Hire us to customize ngx-admin"
type="pantone"
link="https://www.akveo.com/contact?utm_campaign=services%20-%20ngx_admin%20customization%20-%20ngx_admin%20demo%20dashboard&utm_source=ngx_admin&utm_medium=referral&utm_content=iot_dashboard_demo"
linkTitle="Contact us">
</ngx-call-action-card>
</div>
<div class="col-12 col-md-6">
<ngx-call-action-card title="Documentation and customization articles"
type="briefcase"
link="https://akveo.github.io/ngx-admin?utm_campaign=ngx_admin%20-%20landing%20-%20ngx_admin%20demo&utm_source=ngx_admin&utm_medium=referral&utm_content=iot_documentation_panel"
linkTitle="Learn more">
</ngx-call-action-card>
</div>
</div>
<div class="row">
<div class="col-xxxl-3 col-md-6" *ngFor="let statusCard of statusCards">
<ngx-status-card [title]="statusCard.title" [type]="statusCard.type">

View file

@ -1,7 +1,9 @@
import {Component, OnDestroy} from '@angular/core';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { NbThemeService } from '@nebular/theme';
import { takeWhile } from 'rxjs/operators' ;
import { SolarData } from '../../@core/data/solar';
import { AbService } from '../../@core/utils/ab.service';
import { MetadataService } from '../../@core/utils/metadata.service';
interface CardSettings {
title: string;
@ -14,9 +16,10 @@ interface CardSettings {
styleUrls: ['./dashboard.component.scss'],
templateUrl: './dashboard.component.html',
})
export class DashboardComponent implements OnDestroy {
export class DashboardComponent implements OnInit, OnDestroy {
private alive = true;
showCallAction = true;
solarValue: number;
lightCard: CardSettings = {
@ -83,7 +86,9 @@ export class DashboardComponent implements OnDestroy {
};
constructor(private themeService: NbThemeService,
private solarService: SolarData) {
private metaDataService: MetadataService,
private solarService: SolarData,
private abService: AbService) {
this.themeService.getJsTheme()
.pipe(takeWhile(() => this.alive))
.subscribe(theme => {
@ -97,6 +102,17 @@ export class DashboardComponent implements OnDestroy {
});
}
ngOnInit() {
this.metaDataService.updateTitle('Ngx-admin IoT dashboard on Angular 9+ and Nebular');
this.metaDataService.updateDescription('IoT dashboard on Ngx-admin is Angular 9+ Bootstrap 4+ admin' +
' dashboard template. Over 40+ Angular Components and 60+ Usage Examples. Completely FREE and MIT licensed.');
this.metaDataService.updateKeywords('ngx admin, ngx admin dashboard, ngx iot dashboard, ngx-admin iot template');
this.abService.onAbEvent(AbService.VARIANT_HIDE_CALL_ACTION)
.pipe(takeWhile(() => this.alive))
.subscribe(() => this.showCallAction = false );
}
ngOnDestroy() {
this.alive = false;
}

View file

@ -9,7 +9,7 @@
</div>
<nb-card-footer>
<a href="https://akveo.github.io/react-native-ui-kitten?utm_campaign=ui_kitten%20-%20home%20-%20ngx_admin%20code%20embed&utm_source=ngx_admin&utm_medium=embedded&utm_content=iot_dashboard_kitten_card" target="_blank">
<a href="https://akveo.github.io/react-native-ui-kitten/?utm_campaign=ui_kitten%20-%20home%20-%20ngx_admin%20demo&utm_source=ngx_admin&utm_medium=referral&utm_content=iot_dashboard_kitten_card" target="_blank">
<nb-icon icon="globe" pack="eva"></nb-icon>
</a>
<a href="https://itunes.apple.com/us/app/kitten-tricks/id1246143230" target="_blank">

View file

@ -1,3 +1,19 @@
<div class="row" *ngIf="showCallAction">
<div class="col-12 col-md-6">
<ngx-call-action-card title="Hire us to customize ngx-admin"
type="pantone"
link="https://www.akveo.com/contact?utm_campaign=services%20-%20ngx_admin%20customization%20-%20ngx_admin%20demo%20dashboard&utm_source=ngx_admin&utm_medium=referral&utm_content=ecom_dashboard_demo"
linkTitle="Contact us">
</ngx-call-action-card>
</div>
<div class="col-12 col-md-6">
<ngx-call-action-card title="Documentation and customization articles"
type="briefcase"
link="https://akveo.github.io/ngx-admin?utm_campaign=ngx_admin%20-%20landing%20-%20ngx_admin%20demo&utm_source=ngx_admin&utm_medium=referral&utm_content=ecom_docs_panel"
linkTitle="Learn more">
</ngx-call-action-card>
</div>
</div>
<div class="row">
<div class="col-xxl-5">
<div class="row">

View file

@ -1,8 +1,33 @@
import { Component } from '@angular/core';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { takeWhile } from 'rxjs/operators';
import { AbService } from '../../@core/utils/ab.service';
import { MetadataService } from '../../@core/utils/metadata.service';
@Component({
selector: 'ngx-ecommerce',
templateUrl: './e-commerce.component.html',
})
export class ECommerceComponent {
export class ECommerceComponent implements OnInit, OnDestroy {
private alive = true;
showCallAction = true;
constructor (private abService: AbService,
private metaDataService: MetadataService) {}
ngOnInit() {
this.metaDataService.updateTitle('Ngx-admin e-commerce dashboard on Angular 9+ and Nebular.');
this.metaDataService.updateDescription('E-commerce dashboard on Ngx-admin is Angular 9+ Bootstrap 4+ admin' +
' dashboard template. Over 40+ Angular Components and 60+ Usage Examples. Completely FREE and MIT licensed.');
this.metaDataService.updateKeywords('ngx-admin dashboard, ngx ecommerce dashboard, angular 9+');
this.abService.onAbEvent(AbService.VARIANT_HIDE_CALL_ACTION)
.pipe(takeWhile(() => this.alive))
.subscribe(() => this.showCallAction = false );
}
ngOnDestroy() {
this.alive = false;
}
}

View file

@ -12,6 +12,32 @@ export const MENU_ITEMS: NbMenuItem[] = [
icon: 'home-outline',
link: '/pages/iot-dashboard',
},
{
title: 'Backend Integration',
icon: 'settings-outline',
children: [
{
title: 'PHP',
link: '/pages/backend-integration/php',
},
{
title: '.NET Core',
link: '/pages/backend-integration/dot-net-core',
},
{
title: 'Node JS',
link: '/pages/backend-integration/node-js',
},
{
title: 'Java',
link: '/pages/backend-integration/java',
},
{
title: 'E-commerce',
link: '/pages/backend-integration/ecommerce',
},
],
},
{
title: 'FEATURES',
group: true,

View file

@ -18,6 +18,11 @@ const routes: Routes = [{
path: 'iot-dashboard',
component: DashboardComponent,
},
{
path: 'backend-integration',
loadChildren: () => import('./backend-integration/backend-integration.module')
.then(m => m.BackendIntegrationModule),
},
{
path: 'layout',
loadChildren: () => import('./layout/layout.module')

View file

@ -1,18 +1,51 @@
import { Component } from '@angular/core';
import {Component, OnDestroy, OnInit} from '@angular/core';
import { MENU_ITEMS } from './pages-menu';
import {MENU_ITEMS} from './pages-menu';
import {NbMenuItem, NbMenuService} from '@nebular/theme';
import {takeUntil} from 'rxjs/operators';
import {Subject} from 'rxjs';
import {MetadataService} from '../@core/utils/metadata.service';
@Component({
selector: 'ngx-pages',
styleUrls: ['pages.component.scss'],
template: `
<ngx-one-column-layout>
<nb-menu [items]="menu"></nb-menu>
<router-outlet></router-outlet>
</ngx-one-column-layout>
<ngx-sample-layout>
<nb-menu [items]="menu"></nb-menu>
<router-outlet></router-outlet>
</ngx-sample-layout>
`,
})
export class PagesComponent {
export class PagesComponent implements OnInit, OnDestroy {
destroy$: Subject<boolean> = new Subject<boolean>();
constructor(private menuService: NbMenuService,
private metaDataService: MetadataService) {
}
menu = MENU_ITEMS;
ngOnInit() {
if (window['dataLayer']) {
window['dataLayer'].push({'event': 'optimize.activate'});
}
this.menuService
.onItemSelect()
.pipe(takeUntil(this.destroy$))
.subscribe((data: { tag: string; item: NbMenuItem }) => {
if (data.item.title !== 'E-commerce' && data.item.title !== 'IoT Dashboard')
this.metaDataService.updateTitle(`Ngx-admin dashboard by Akveo | ${data.item.title}`);
this.metaDataService.updateDescription('Ngx-admin is Angular 9+ Bootstrap 4+ admin dashboard template.' +
' Over 40+ Angular Components, 60+ Usage Examples and UI features.');
this.metaDataService.updateKeywords('ngx-admin, ngx admin dashboard features, ngx admin forms,' +
' ngx-admin maps, ngx-admin UI features, ngx-admin tables, ngx admin overlays, ngx-admin extra components');
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}

View file

@ -8,7 +8,7 @@
<nb-icon *ngFor="let icon of evaIcons" [icon]="icon" pack="eva"></nb-icon>
</nb-card-body>
<nb-card-footer>
<a href="https://akveo.github.io/eva-icons/?utm_campaign=eva_icons%20-%20home%20-%20ngx_admin%20code%20embed&utm_source=ngx_admin&utm_medium=embedded&utm_content=icons_eva_icons_card" target="_blank">See all Eva Icons</a>
<a href="https://akveo.github.io/eva-icons?utm_campaign=eva_icons%20-%20home%20-%20ngx_admin%20demo&utm_source=ngx_admin&utm_medium=referral&utm_content=eva_icons_card" target="_blank">See all Eva Icons</a>
</nb-card-footer>
</nb-card>
</div>

View file

@ -0,0 +1,21 @@
import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {NgxStarterComponent} from './starter.component';
const routes: Routes = [{
path: '',
component: NgxStarterComponent,
children: [
{
path: '',
component: NgxStarterComponent,
},
],
}];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class StarterRoutingModule {
}

View file

@ -0,0 +1,53 @@
<nb-layout windowMode>
<nb-layout-header fixed>
<div class="header-container">
<div class="logo-container">
<p [ngClass]="{ 'material-text-color': (materialTheme$ | async) }" 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 [ngClass]="{ 'material-text-color': (materialTheme$ | async) }" 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 [ngClass]="{ 'material-text-color': (materialTheme$ | async) }" 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 [ngClass]="{ 'material-text-color': (materialTheme$ | async) }">contact@akveo.com</span>
</a>
</nb-action>
</nb-actions>
</div>
</nb-layout-header>
<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.jpg"
class="theme-preview"
alt="{{theme.name}} Theme"/>
</nb-card-body>
</nb-card>
</ng-container>
</nb-layout-column>
<nb-layout-footer fixed>
<ngx-footer></ngx-footer>
</nb-layout-footer>
</nb-layout>

View file

@ -0,0 +1,178 @@
@import '~bootstrap/scss/mixins/breakpoints';
@import '~@nebular/theme/styles/global/breakpoints';
@import '../@theme/styles/themes';
@include nb-install-component() {
.material-text-color {
color: nb-theme(color-basic-100);
}
img {
width: 100%;
object-fit: contain;
height: auto;
vertical-align: top;
}
h4 {
text-align: center;
width: 100%;
height: 35px;
margin-bottom: 36px;
}
nb-layout-column {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
justify-content: space-between;
align-content: flex-start;
}
nb-card {
overflow: hidden;
cursor: pointer;
width: 32%;
}
nb-card-body {
padding: 0;
}
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) {
}
@include media-breakpoint-down(lg) {
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

@ -0,0 +1,76 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { NbMediaBreakpoint } from '@nebular/theme';
import { Router } from '@angular/router';
import { AnalyticsService } from '../@core/utils';
import { CurrentThemeService } from '../@core/utils/theme.service';
import { Observable } from 'rxjs';
import { MetadataService } from '../@core/utils/metadata.service';
@Component({
selector: 'ngx-starter',
templateUrl: './starter.component.html',
styleUrls: ['./starter.component.scss'],
})
export class NgxStarterComponent implements OnDestroy, OnInit {
breakpoint: NbMediaBreakpoint;
breakpoints: any;
public readonly materialTheme$ = new Observable(subscriber => {
const themeName: string = this.currentThemeService.getCurrentTheme();
subscriber.next(themeName.startsWith('material'));
});
themes = [
{
value: 'material-light',
name: 'Material Light',
},
{
value: 'material-dark',
name: 'Material Dark',
},
{
value: 'default',
name: 'Light',
},
{
value: 'dark',
name: 'Dark',
},
{
value: 'corporate',
name: 'Corporate',
},
{
value: 'cosmic',
name: 'Cosmic',
},
];
constructor(private router: Router,
private currentThemeService: CurrentThemeService,
private analytics: AnalyticsService,
private metadataService: MetadataService,
) {}
// tslint:disable:max-line-length
ngOnInit(): void {
this.metadataService.updateTitle('Ngx-admin themes for e-commerce dashboard on Angular 9+ and Nebular');
this.metadataService.updateDescription('Choose a theme for ngx-admin e-commerce dashboard: Material, Light and Dark, Cosmic and Corporate.');
this.metadataService.updateKeywords('Ngx-admin themes, material theme, ngx-admin cosmic, ngx-admin corporate theme, dark theme ngx-admin');
}
// tslint:enable:max-line-length
navigate(themeName: string) {
this.currentThemeService.setCurrentTheme(themeName);
this.router.navigate(['/pages/dashboard'], {queryParams: {theme: themeName}});
}
trackEmailClick() {
this.analytics.trackEvent('clickContactEmail', 'click');
}
ngOnDestroy() {
}
}

View file

@ -0,0 +1,27 @@
import {NgModule} from '@angular/core';
import {StarterRoutingModule} from './starter-routing.module';
import {NgxStarterComponent} from './starter.component';
import {NbActionsModule, NbButtonModule, NbCardModule, NbIconModule, NbLayoutModule} from '@nebular/theme';
import {ThemeModule} from '../@theme/theme.module';
const NB_MODULES = [
NbIconModule,
NbLayoutModule,
NbCardModule,
NbButtonModule,
];
@NgModule({
imports: [
StarterRoutingModule,
...NB_MODULES,
ThemeModule,
NbActionsModule,
],
declarations: [
NgxStarterComponent,
NgxStarterComponent,
],
})
export class StarterModule {
}