+ Looking for a way to integrate Angular ngx-admin with .NET, Node.js, Ruby or PHP? We are here to save your time on painful setup, configuration and routing tasks.
+ Choose starter kit bundle based on a technology of your choice below.
+
+ Subscribe to get notified about new versions of ngx-admin and other cool projects that we are working on
+
+
+
+
+
+
+
+ I agree to get news on what’s going on around Akveo products and community.
+ The administrator processes data following the Privacy Policy. I understand that I can opt out at any time
+
+ Material admin theme based on the most popular Angular dashboard template - ngx-admin.
+ Included: Angular 9+,
+ Nebular and
+ Eva Design System.
+ Free for personal and commercial usage.
+
+
+ Get material ngx-admin integrated with backend technology of your choice.
+
+ Check out our store.
+
+
+
+
diff --git a/docs/app/pages/home/main-info-section/material-admin-main-info/material-info.component.ts b/docs/app/pages/home/main-info-section/material-admin-main-info/material-info.component.ts
new file mode 100644
index 00000000..917a60c8
--- /dev/null
+++ b/docs/app/pages/home/main-info-section/material-admin-main-info/material-info.component.ts
@@ -0,0 +1,19 @@
+import { Component, OnInit } from '@angular/core';
+import { MetadataService } from '../../../../../../src/app/@core/utils/metadata.service';
+
+@Component({
+ selector: 'ngx-material-admin-info',
+ templateUrl: './material-info.component.html',
+ styleUrls: ['./../main-info-section.component.scss'],
+})
+export class MaterialAdminInfoComponent implements OnInit {
+
+ constructor(private metaDataService: MetadataService) {}
+
+ ngOnInit(): void {
+ this.metaDataService.updateTitle('Ngx-admin material dashboard template based on Angular 9+ and Bootstrap 4+');
+ this.metaDataService.updateDescription('Ngx-admin material works perfectly with Angular Material and Nebular.' +
+ ' Over 40+ Angular Components and 60+ Usage Examples.Take the best from both!');
+ this.metaDataService.updateKeywords('Ngx-admin material theme, ngx-admin material dashboard, ngx-admin material');
+ }
+}
diff --git a/docs/app/pages/home/material-features/material-features.component.html b/docs/app/pages/home/material-features/material-features.component.html
new file mode 100644
index 00000000..c9f9acce
--- /dev/null
+++ b/docs/app/pages/home/material-features/material-features.component.html
@@ -0,0 +1,31 @@
+
+ Features and benefits
+
+
+
+
+
1
+
+ The most popular and trusted Angular open source dashboard template is out there. Used by hundreds of thousands developers worldwide and Fortune 500 companies*
+
+
+
+
2
+
+ Over 40+ Angular Components and 60+ Usage Examples. Kick off your project and save money by using ngx-admin.
+
+
+
+
3
+
+ Already using ngx-admin and willing to switch to material theme? Material theme is backward-compatible. Check out the article describing how to do that
+
+
+
+
4
+
+ ngx-admin material works perfectly with Angular Material and Nebular. Take the best from both!
+
+ Nebular is a great toolkit if you build Rich UI web applications based on Angular. Complete with a set of native Angular components, themeable components, authentication and security layers that are easily configurable to your API. Nebular offers a world of possibilities
+
+ Eva Icons is a pack of more than 480 beautifully crafted Open Source icons. Download for desktop and use them in your creations for Web, iOS, and Android. The icons are available in several formats: PNG, SVG, font, Sketch
+
+ Save more than $33,000 using ngx-admin.
+ According to our research ngx-admin optimizes 480 hours of development time. Taking the average hourly rate of a front-end engineer in the US – $70
+
+
+
+
2
+
+ Create a custom experience by working with one of 3 themes or edit just a few custom variables. Need help? Check out our article, where we describe how you can create a different look in just 2 minutes
+
+
+
+
3
+
+ It’s completely FREE and MIT licensed.
+ You don’t have to worry about the legal stuff, ngx-admin is free for personal and commercial purposes, so you can use it to build any product
+
+
+
+
4
+
+ Do you need more? We can customize!
+ Visit our homepage or simply leave us a note at contact@akveo.com. We’re available for hire and can create custom solutions just for you
+
+
diff --git a/docs/app/shared/components/material-theme-link/material-theme-link.component.scss b/docs/app/shared/components/material-theme-link/material-theme-link.component.scss
new file mode 100644
index 00000000..6f101d09
--- /dev/null
+++ b/docs/app/shared/components/material-theme-link/material-theme-link.component.scss
@@ -0,0 +1,37 @@
+
+@import '../../../@theme/styles/themes';
+@import '~@nebular/theme/styles/global/breakpoints';
+
+@include nb-install-component() {
+ $menu-item-fg: nb-theme(color-fg-heading-light);
+ $menu-item-fg-active: nb-theme(header-menu-fg-active);
+
+ :host {
+ display: flex;
+ align-items: center;
+ padding-right: 32px;
+ }
+
+ a {
+ text-decoration: none;
+ font-weight: 600;
+ font-size: 13px;
+ line-height: 1.5rem;
+ padding: 0.675rem 1.375rem;
+ color: $menu-item-fg;
+ display: block;
+
+ &:hover, &.active, &:focus {
+ color: $menu-item-fg-active;
+ outline: none !important;
+ }
+ }
+}
+
+.material-theme-popover {
+ margin: 0;
+ padding: 1rem 2rem;
+ color: #ff4d6b;
+ font-weight: 600;
+ font-size: 1.1rem;
+}
diff --git a/docs/app/shared/components/material-theme-link/material-theme-link.component.ts b/docs/app/shared/components/material-theme-link/material-theme-link.component.ts
new file mode 100644
index 00000000..a73339ab
--- /dev/null
+++ b/docs/app/shared/components/material-theme-link/material-theme-link.component.ts
@@ -0,0 +1,74 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ */
+
+import { Component, ViewChild, AfterViewInit, Input, OnDestroy } from '@angular/core';
+import { coerceBooleanProperty } from '@angular/cdk/coercion';
+import { Subject, merge } from 'rxjs';
+import { map, take, takeUntil } from 'rxjs/operators';
+import { NbMediaBreakpointsService, NbMediaBreakpoint, NbPopoverDirective, NbThemeService } from '@nebular/theme';
+
+@Component({
+ selector: 'ngx-material-theme-link',
+ templateUrl: './material-theme-link.component.html',
+ styleUrls: ['./material-theme-link.component.scss'],
+})
+export class MaterialThemeLinkComponent implements AfterViewInit, OnDestroy {
+
+ private destroy$ = new Subject();
+ private hidePopover$ = new Subject();
+
+ showPopover: boolean = false;
+
+ @Input()
+ set withPopover(value: any) {
+ this.showPopover = coerceBooleanProperty(value);
+ }
+
+ @ViewChild(NbPopoverDirective) popover: NbPopoverDirective;
+
+ constructor(
+ private breakpointService: NbMediaBreakpointsService,
+ private themeService: NbThemeService,
+ ) {}
+
+ ngAfterViewInit(): void {
+ if (!this.showPopover) {
+ return;
+ }
+
+ this.themeService.onMediaQueryChange()
+ .pipe(
+ map(([, currentBreakpoint]: NbMediaBreakpoint[]) => this.shouldShowPopover(currentBreakpoint)),
+ takeUntil(merge(this.destroy$, this.hidePopover$)),
+ )
+ .subscribe((showPopover: boolean) => {
+ if (showPopover) {
+ this.popover.show();
+ } else {
+ this.popover.hide();
+ }
+ });
+
+ this.hidePopover$
+ .pipe(
+ take(1),
+ takeUntil(this.destroy$),
+ )
+ .subscribe(() => this.popover.hide());
+ }
+
+ ngOnDestroy() {
+ this.destroy$.next();
+ }
+
+ hidePopover(): void {
+ this.hidePopover$.next();
+ }
+
+ private shouldShowPopover(breakpoint: NbMediaBreakpoint): boolean {
+ return breakpoint.width >= this.breakpointService.getByName('is').width;
+ }
+}
diff --git a/docs/app/shared/components/premium-form/premium-form.component.html b/docs/app/shared/components/premium-form/premium-form.component.html
new file mode 100644
index 00000000..45436c7b
--- /dev/null
+++ b/docs/app/shared/components/premium-form/premium-form.component.html
@@ -0,0 +1,11 @@
+
+
+ Premium
+
+
+
+
+
+
diff --git a/docs/app/shared/components/premium-form/premium-form.component.scss b/docs/app/shared/components/premium-form/premium-form.component.scss
new file mode 100644
index 00000000..a1a8c31c
--- /dev/null
+++ b/docs/app/shared/components/premium-form/premium-form.component.scss
@@ -0,0 +1,31 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ */
+
+@import '~@nebular/theme/styles/global/breakpoints';
+
+@import '../../../@theme/styles/themes';
+
+@include nb-install-component() {
+
+ nb-card {
+ width: 30rem;
+ }
+
+ nb-card-header {
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ }
+
+ .close-icon {
+ margin-left: auto;
+ padding: 0;
+ }
+
+ ::ng-deep .hs-custom-form .hs-submit-btn {
+ margin-top: 1rem;
+ }
+}
diff --git a/docs/app/shared/components/premium-form/premium-form.component.ts b/docs/app/shared/components/premium-form/premium-form.component.ts
new file mode 100644
index 00000000..c3d75abe
--- /dev/null
+++ b/docs/app/shared/components/premium-form/premium-form.component.ts
@@ -0,0 +1,35 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ */
+
+import { AfterViewInit, Component } from '@angular/core';
+import { NbDialogRef } from '@nebular/theme';
+
+@Component({
+ selector: 'ngx-premium-form',
+ templateUrl: './premium-form.component.html',
+ styleUrls: ['./premium-form.component.scss'],
+})
+export class PremiumFormComponent implements AfterViewInit {
+
+
+ constructor(protected ref: NbDialogRef) {
+ }
+
+ ngAfterViewInit() {
+ hbspt.forms.create({
+ portalId: '2452262',
+ formId: 'b066428e-c41a-4dce-bbc2-5690cf084a8f',
+ target: '#hubspotForm',
+ submitButtonClass: 'hs-submit-btn btn',
+ css: '',
+ cssClass: 'hs-custom-form',
+ });
+ }
+
+ closeDialog() {
+ this.ref.close();
+ }
+}
diff --git a/docs/app/shared/landing-shared.module.ts b/docs/app/shared/landing-shared.module.ts
new file mode 100644
index 00000000..df1c34a2
--- /dev/null
+++ b/docs/app/shared/landing-shared.module.ts
@@ -0,0 +1,39 @@
+import { NgModule } from '@angular/core';
+import {NbBadgeModule, NbButtonModule, NbCardModule, NbDialogModule, NbPopoverModule} from '@nebular/theme';
+import { MaterialThemeLinkComponent } from './components/material-theme-link/material-theme-link.component';
+import { CapitalizePipe } from './pipes/capitalize.pipe';
+import { EvaIconsPipe } from './pipes/eva-icons.pipe';
+import { RouterModule } from '@angular/router';
+import { DownloadFormComponent } from './components/download-form/download-form.component';
+import { PremiumFormComponent } from './components/premium-form/premium-form.component';
+
+const component = [
+ MaterialThemeLinkComponent,
+ DownloadFormComponent,
+ PremiumFormComponent,
+];
+const pipes = [
+ CapitalizePipe,
+ EvaIconsPipe,
+];
+
+@NgModule({
+ imports: [
+ RouterModule,
+ NbPopoverModule,
+ NbBadgeModule,
+ NbCardModule,
+ NbDialogModule.forChild(),
+ NbButtonModule,
+ ],
+ declarations: [
+ ...component,
+ ...pipes,
+ ],
+ exports: [
+ NbPopoverModule,
+ ...component,
+ ...pipes,
+ ],
+})
+export class LandingSharedModule {}
diff --git a/docs/app/shared/pipes/capitalize.pipe.ts b/docs/app/shared/pipes/capitalize.pipe.ts
new file mode 100644
index 00000000..61d5e589
--- /dev/null
+++ b/docs/app/shared/pipes/capitalize.pipe.ts
@@ -0,0 +1,11 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({ name: 'ngxCapitalize' })
+export class CapitalizePipe implements PipeTransform {
+
+ transform(input: string): string {
+ return input && input.length
+ ? (input.charAt(0).toUpperCase() + input.slice(1).toLowerCase())
+ : input;
+ }
+}
diff --git a/docs/app/shared/pipes/eva-icons.pipe.ts b/docs/app/shared/pipes/eva-icons.pipe.ts
new file mode 100644
index 00000000..0dd7ce42
--- /dev/null
+++ b/docs/app/shared/pipes/eva-icons.pipe.ts
@@ -0,0 +1,50 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ */
+
+import { DomSanitizer } from '@angular/platform-browser';
+import { Pipe, PipeTransform } from '@angular/core';
+import { icons } from 'eva-icons';
+
+@Pipe({ name: 'eva' })
+export class EvaIconsPipe implements PipeTransform {
+
+ private defaultOptions = {
+ height: 24,
+ width: 24,
+ fill: 'inherit',
+ animationHover: true,
+ animationInfinity: false,
+ };
+
+ constructor(private sanitizer: DomSanitizer) {}
+
+ transform(icon: string,
+ options: {
+ height: number;
+ width: number;
+ fill?: string;
+ animationType?: string;
+ animationHover?: boolean;
+ animationInfinity?: boolean;
+ },
+ ) {
+ const mergedOptions = {
+ ...this.defaultOptions,
+ ...options,
+ };
+ const { width, height, fill, animationType, animationHover, animationInfinity } = mergedOptions;
+ const animation = animationType ?
+ { type: animationType, hover: animationHover, infinite: animationInfinity } :
+ null;
+
+ return this.sanitizer.bypassSecurityTrustHtml(icons[icon].toSvg({
+ width,
+ height,
+ fill,
+ animation,
+ }));
+ }
+}
diff --git a/docs/articles/.gitkeep b/docs/articles/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/docs/articles/backend-integration.md b/docs/articles/backend-integration.md
new file mode 100644
index 00000000..20377a8f
--- /dev/null
+++ b/docs/articles/backend-integration.md
@@ -0,0 +1,82 @@
+# Backend Integration
+
+This section describes approaches of integration of ngx-admin application with backend API. Despite we understand that every backend is really different, we think that we can cover several most commonly used ways.
+
+
+## Integration with JSON REST server
+
+Despite there's an option to do CORS requests to API server directly, we don't advise to do so. This way has disadvantages in terms of security and performance. In terms of security when you do CORS request you basically expose your API server URL to everybody. Your API server should take additional measures to make sure some URLs are not accessible, because it is exposed to the web. As for performance, CORS requests require to send preflight OPTIONS request before each HTTP request. This adds additional HTTP overhead.
+
+The solution we suggest is to use proxy for your API server. In this case you can make your app accessible through some sub-url. For example, if your application's hosted under url `website.com` and your index file is located at `website.com/index.html`, you can make your API root accessible on `website.com/api`. This is well supported by angular-cli/webpack-dev-server for development setup and by web servers for production setup. Let's review these setups:
+
+
+## angular-cli/webpack-dev-server setup
+
+There's not so much needs to be done to proxy your api using angular-cli. You can read detailed documentation in their docs.
+But the most important topics are:
+
+You should create `proxy.conf.json` file in your application root. The file should contain something like below:
+```json
+{
+ "/api": {
+ "target": "http://localhost:3000",
+ "secure": false
+ }
+}
+```
+
+In this case you should put URL of your API server instead of `http://localhost:3000`.
+
+After that you need to run your angular-cli application using following command
+```bash
+ng serve --proxy-config proxy.conf.json
+```
+That's it. Now you can access `/api` URL from your ngx-admin application and your requests will be forwarded to your API server.
+
+
+## Production setup
+
+Production setup is not much different from development setup. The only difference is that usually you don't use there angular-cli or webpack-dev-server to host your HTML/CSS/JS. Usually we all use some web server for that. At Akveo we mostly use [nginx](https://nginx.org/en/) for this use case. Below there is a sample configuration for this particular web server. For others it is not that much different.
+
+Usually you create new virtual host with some similar configuration:
+
+```nginx
+server {
+ listen 80;
+ server_name website.com;
+
+ root /yourAngularAppDistPath;
+ index index.html index.htm;
+ etag on;
+
+ location / {
+ index index.html;
+ try_files $uri /index.html;
+ }
+}
+```
+
+The only thing you need to add is proxy-pass to `/api` URL like below:
+
+```nginx
+server {
+ listen 80;
+ server_name website.com;
+
+ root /yourAngularAppDistPath;
+ index index.html index.htm;
+ etag on;
+
+ location / {
+ index index.html;
+ try_files $uri /index.html;
+ }
+
+ location /api {
+ proxy_pass http://localhost:3000/;
+ proxy_set_header Host $host;
+ }
+}
+```
+
+That's it. Now your API server works on production as well.
diff --git a/docs/articles/concept-theme-system.md b/docs/articles/concept-theme-system.md
new file mode 100644
index 00000000..add79c01
--- /dev/null
+++ b/docs/articles/concept-theme-system.md
@@ -0,0 +1,124 @@
+# Theme System
+
+Nebular Theme System is a set of rules we put into how SCSS files and variables are organized to achieve the following goals:
+
+- ability to flexibly change looks & feel of the application by managing variables, without changing SCSS itself;
+- ability to switch between visual themes in a runtime without reloading the page;
+- support of CSS-variables (implemented partially).
+
+
+## Theme Map
+
+Each theme is represented as an SCSS map with a list of key-value pairs:
+
+```scss
+$theme: (
+ font-main: unquote('"Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif'),
+ font-secondary: font-main,
+
+ font-weight-thin: 200,
+ font-weight-light: 300,
+ font-weight-normal: 400,
+ font-weight-bolder: 500,
+ font-weight-bold: 600,
+ font-weight-ultra-bold: 800,
+
+ base-font-size: 16px,
+
+ font-size-xlg: 1.25rem,
+ font-size-lg: 1.125rem,
+ font-size: 1rem,
+ font-size-sm: 0.875rem,
+ font-size-xs: 0.75rem,
+
+ radius: 0.375rem,
+ padding: 1.25rem,
+ margin: 1.5rem,
+ line-height: 1.25,
+
+ ...
+```
+Where _key_ - is a variable name, and _value_ - is a raw SCSS value (color, string, etc) or **parent variable name**, so that you can inherit values from different variables:
+
+```scss
+$theme: (
+ font-main: unquote('"Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif'),
+ font-secondary: font-main,
+```
+Here `font-secondary` inherits its value from `font-main`.
+
+
+## Component Variables
+
+Then, for each component of the Nebular Components, there is a list of variables you can change.
+For example - header component variables:
+
+```scss
+ ...
+
+ header-font-family: font-secondary,
+ header-font-size: font-size,
+ header-line-height: line-height,
+ header-fg: color-fg-heading,
+ header-bg: color-bg,
+ header-height: 4.75rem,
+ header-padding: 1.25rem,
+ header-shadow: shadow,
+
+ ...
+```
+As you can see, you have 8 variables for a pretty simple component and from the other side, 6 of them are inherited from the default values.
+It means that if you want to create a new theme with a united look & feel of the components - in most cases you would need to change around 10 generic variables, such as `color-bg`, `shadow`, etc.
+to change the UI completely.
+
+List of component style variables is specified in the component documentation, for example [styles for header component](docs/components/layout/theme#nblayoutheadercomponent).
+
+
+## Variables Usage
+
+Now, if you want to use the variables in your custom style files, all you need to do (of course, after the [successful setup of the Theme System](docs/guides/enable-theme-system) is to call `nb-theme(var-name)` function:
+
+```scss
+@import '../../../@theme/styles/themes';
+
+:host {
+
+ background: nb-theme(card-bg); // and use it
+}
+```
+Depending on the currently enabled theme and the way `card-bg` inherited in your theme, you will get the right color.
+
+
+## Built-in themes
+
+Currently, there are 3 built-in themes:
+- `default` - clean white theme
+- `cosmic` - dark theme
+- `corporate` - firm business theme
+
+Themes can also be inherited from each other, `cosmic`, for instance, is inherited from the `default` theme.
+
+
+## Magic of multiple themes with hot-reload
+
+As you can see from the [ngx-admin demo](https://www.akveo.com/ngx-admin?utm_campaign=ngx_admin%20-%20demo%20-%20ngx_admin%20docs&utm_source=ngx_admin&utm_medium=referral&utm_content=docs_theme_system_concept), you can switch themes in the runtime without reloading the page.
+It is useful when you have multiple visual themes per user role or want to provide your user with such a configuration so that he can decide which theme works best for him.
+The only requirement for the feature to work is to wrap all of your component styles into special mixin `nb-install-component` and use `nb-theme` to get the right value:
+
+```scss
+@import '../../../@theme/styles/themes';
+
+@include nb-install-component() {
+ background: nb-theme(card-bg); // now, for each theme registered the corresponding value will be inserted
+
+ .container {
+ background: nb-theme(color-bg);
+ font-weight: nb-theme(font-weight-bold);
+ }
+}
+```
+
+
+## Related Articles
+
+- [Change Theme](docs/guides/change-theme)
diff --git a/docs/articles/index.md b/docs/articles/index.md
new file mode 100644
index 00000000..0514b99a
--- /dev/null
+++ b/docs/articles/index.md
@@ -0,0 +1,50 @@
+# What is ngx-admin?
+
+ngx-admin is a front-end admin dashboard template based on Angular 9+, Bootstrap 4+ and Nebular. That means all the data you can see on graphs, charts and tables is mocked in Javascript so you can use the backend of your choice with no limitations.
+
+
+## How can it help me?
+
+We believe that at the moment a lot of business applications have administration/management interfaces inside of them. Sometimes it’s not that obvious, but a lot of web applications have dashboards with panels, charts analytics, etc.
+
+
+ngx-admin aims to bootstrap the development of your product and provide an ecosystem for building production-ready application or prototypes.
+
+Frameworks like Bootstrap provide a number of components, but usually it’s not enough to build a real-world app. This template comes with lots of popular UI components with a unified color scheme, plus it is based on a modern Angular framework and has a flexible component based structure.
+
+You can also use ngx-admin for the purpose of learning Angular.
+
+
+## List of features
+
+- Angular 9+ & Typescript
+- Bootstrap 4+ & SCSS
+- Responsive layout
+- RTL support
+- High resolution
+- Flexibly configurable themes with **hot-reload** (3 themes included)
+- Authentication module with multiple providers
+- Lots of awesome features:
+ - Buttons
+ - Modals
+ - Popovers
+ - Icons
+ - Typography
+ - Animated searches
+ - Forms
+ - Tabs
+ - Notifications
+ - Tables
+ - Maps
+ - Charts
+ - Editors
+
+And many more!
+
+
+## Assumptions
+
+This documentation assumes that you are already familiar with JavaScript/TypeScript, Angular, CSS and Bootstrap.
+
+## Have questions?
+Didn't find something here? Look through the issues or ask on Stack Overflow.
diff --git a/docs/articles/install-starter-kit.md b/docs/articles/install-starter-kit.md
new file mode 100644
index 00000000..66e9ddef
--- /dev/null
+++ b/docs/articles/install-starter-kit.md
@@ -0,0 +1,63 @@
+# Install ngx-admin
+
+Please note, that **ngx-admin** is just a frontend application. Backend integration can be done relatively simple, but you should be aware that all the data is mocked using JavaScript objects.
+If you want the data to be dynamic, you should consider developing a backend integration by your own.
+The Nebular team doesn't consider providing generic integration layer as a part of this project because every backend API has a different structure in terms of data format and URLs.
+
+
+## Install tools
+
+To install ngx-admin on your machine you need to have the following tools installed:
+- Git - https://git-scm.com
+- Node.js - https://nodejs.org. Please note the **version** should be **>=8**
+- Npm - Node.js package manager, comes with Node.js. Please make sure npm **version** is **>=5**
+- You might also need some specific native packages depending on your operating system like `build-essential` on Ubuntu
+
+
+
Warning!
+
+ Please note that **it is not possible** to build ngx-admin **without these tools** and it will not be possible because of the way how Angular is built.
+
+
+
+
+## Download the code
+
+When you completed tools setup, you need to download the code of ngx-admin application. The easiest way to do that is to clone GitHub repository:
+```bash
+git clone https://github.com/akveo/ngx-admin.git
+```
+
+After clone is completed, you need to install npm modules:
+```bash
+cd ngx-admin && npm i
+```
+
+
Warning!
+
+ Please make sure that installation process successfully completed without errors.
+
+
+
+
+## Run local copy
+
+To run a local copy in development mode, execute:
+
+```bash
+npm start
+```
+
+Go to http://0.0.0.0:4200 or http://localhost:4200 in your browser.
+
+
+## Production bundle
+
+To create a bundle in production mode, execute:
+
+```bash
+npm run build:prod
+```
+
+This will clear up your `dist` folder (where release files are located) and generate a release build.
+Now you can copy the sources from the `dist` folder and use it with any backend framework or simply [put it under a web server](docs/getting-started/server-deployment).
diff --git a/docs/articles/server-deployment.md b/docs/articles/server-deployment.md
new file mode 100644
index 00000000..097cde0c
--- /dev/null
+++ b/docs/articles/server-deployment.md
@@ -0,0 +1,11 @@
+# Server Deployment
+
+Though in the development Nebular app consists of a number of TypeScript, SASS, etc files, the built package is just a bunch HTML/JavaScript/CSS files.
+No other processing is needed to get them running in a browser.
+So to deploy the app you basically need two simple steps:
+
+- Build your app with `npm run build:prod`
+- Copy the output from the `dist` folder under a web-server of your choice.
+
+More details on how to setup your web-server to better serve the application can be found on Angular Documentation website, under Server Configuration section.
+
diff --git a/docs/articles/start.md b/docs/articles/start.md
new file mode 100644
index 00000000..a65f29f8
--- /dev/null
+++ b/docs/articles/start.md
@@ -0,0 +1,17 @@
+# Where to start?
+
+Nebular is a set of modules for Angular. Despite it is not required to know Angular framework to set up your first Nebular project, it is highly recommended to go through the Angular tutorial beforehand and be familiar with basic Angular concepts.
+
+
+## Quickstart tutorials
+
+Based on a current setup of your project and your goals, there are two starting points:
+
+- **[Starting based on our Nebular Admin starter kit](docs/guides/install-based-on-starter-kit)** Consider this tutorial if you are building admin or any other back-office application and you need a template as a good starting kit.
+- **[Adding into existing Angular Project](docs/guides/add-into-existing-project)** This tutorial explains how to use Nebular if you already have some Angular code as starting app from scratch.
+
+Please consider creating an issue on GitHub if your use case is not described above. But we kindly ask to always look through the documentation and the list of existing issues first.
+
+## I'm new to Angular or web development in general
+
+Quite often we receive emails and messages from people who ask us for the advice we can give them if they are completely new to software engineering and/or Angular in particular. Well, we can't say that there's some general way, unfortunately. Each advice should be aimed at a particular person, his current skills set and goals. That's why we believe that each person knows better for himself. But in any case, there are multiple resources like https://www.coursera.org/ or https://egghead.io/ which focus on providing online education.
diff --git a/docs/articles/theme-change.md b/docs/articles/theme-change.md
new file mode 100644
index 00000000..8beee83f
--- /dev/null
+++ b/docs/articles/theme-change.md
@@ -0,0 +1,54 @@
+# Change Current Theme
+
+Nebular Theme System provides 3 color schemes out of the box - `default`, `corporate` and `cosmic`. It is both possible to change the theme statically and dynamically during the runtime.
+
+
+## Switch from Cosmic to Default
+It is extremely simple to replace a theme from one to another.
+All you need to do is to find your `NbThemeModule.forRoot` declaration and change a value of the `name` setting:
+
+```ts
+ @NgModule({
+ imports: [
+ // ...
+ NbThemeModule.forRoot({ name: 'default' }),
+ ],
+ }
+```
+
+
+## Runtime Theme Switch
+In case you want to have a better control when a theme is changed, or for instance need to change it based on a user role,
+it is possible to dynamically tell Nebular which theme should be enabled.
+`NbThemeService` is our friend in this case and particularly the `changeTheme` method:
+
+```ts
+
+ // ...
+ constructor(private themeService: NbThemeService) {
+ this.themeService.changeTheme('corporate');
+ }
+
+```
+
+
+## Listen to Theme Change
+And of course it is possible to subscribe to an event when the current theme gets changed so that you can adjust something in your code accordingly:
+
+```ts
+
+ // ...
+ constructor(private themeService: NbThemeService) {
+
+ this.themeService.onThemeChange()
+ .subscribe((theme: any) => {
+ console.log(`Theme changed to ${theme.name}`);
+ });
+ }
+
+```
+
+
+## Related Articles
+
+- [Theme System](docs/guides/theme-system)
diff --git a/docs/assets/.gitkeep b/docs/assets/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/docs/assets/fonts/feather/feather.eot b/docs/assets/fonts/feather/feather.eot
new file mode 100644
index 00000000..58371d90
Binary files /dev/null and b/docs/assets/fonts/feather/feather.eot differ
diff --git a/docs/assets/fonts/feather/feather.svg b/docs/assets/fonts/feather/feather.svg
new file mode 100644
index 00000000..5dda143b
--- /dev/null
+++ b/docs/assets/fonts/feather/feather.svg
@@ -0,0 +1,849 @@
+
+
+
+
diff --git a/docs/assets/fonts/feather/feather.ttf b/docs/assets/fonts/feather/feather.ttf
new file mode 100644
index 00000000..0b33dac7
Binary files /dev/null and b/docs/assets/fonts/feather/feather.ttf differ
diff --git a/docs/assets/fonts/feather/feather.woff b/docs/assets/fonts/feather/feather.woff
new file mode 100644
index 00000000..9b03a72a
Binary files /dev/null and b/docs/assets/fonts/feather/feather.woff differ
diff --git a/docs/assets/fonts/helvetica-neue/HelveticaNeue-Bold.eot b/docs/assets/fonts/helvetica-neue/HelveticaNeue-Bold.eot
new file mode 100644
index 00000000..7118d514
Binary files /dev/null and b/docs/assets/fonts/helvetica-neue/HelveticaNeue-Bold.eot differ
diff --git a/docs/assets/fonts/helvetica-neue/HelveticaNeue-Bold.svg b/docs/assets/fonts/helvetica-neue/HelveticaNeue-Bold.svg
new file mode 100644
index 00000000..fdf71229
--- /dev/null
+++ b/docs/assets/fonts/helvetica-neue/HelveticaNeue-Bold.svg
@@ -0,0 +1,5856 @@
+
+
+
diff --git a/docs/assets/fonts/helvetica-neue/HelveticaNeue-Bold.ttf b/docs/assets/fonts/helvetica-neue/HelveticaNeue-Bold.ttf
new file mode 100644
index 00000000..c34caac2
Binary files /dev/null and b/docs/assets/fonts/helvetica-neue/HelveticaNeue-Bold.ttf differ
diff --git a/docs/assets/fonts/helvetica-neue/HelveticaNeue-Bold.woff b/docs/assets/fonts/helvetica-neue/HelveticaNeue-Bold.woff
new file mode 100644
index 00000000..eebe59b2
Binary files /dev/null and b/docs/assets/fonts/helvetica-neue/HelveticaNeue-Bold.woff differ
diff --git a/docs/assets/fonts/icomoon.eot b/docs/assets/fonts/icomoon.eot
new file mode 100644
index 00000000..4264fffa
Binary files /dev/null and b/docs/assets/fonts/icomoon.eot differ
diff --git a/docs/assets/fonts/icomoon.svg b/docs/assets/fonts/icomoon.svg
new file mode 100644
index 00000000..558b8316
--- /dev/null
+++ b/docs/assets/fonts/icomoon.svg
@@ -0,0 +1,12 @@
+
+
+
\ No newline at end of file
diff --git a/docs/assets/fonts/icomoon.ttf b/docs/assets/fonts/icomoon.ttf
new file mode 100644
index 00000000..5b06e9fa
Binary files /dev/null and b/docs/assets/fonts/icomoon.ttf differ
diff --git a/docs/assets/fonts/icomoon.woff b/docs/assets/fonts/icomoon.woff
new file mode 100644
index 00000000..7544c64a
Binary files /dev/null and b/docs/assets/fonts/icomoon.woff differ
diff --git a/docs/assets/fonts/small-social/small-social.eot b/docs/assets/fonts/small-social/small-social.eot
new file mode 100644
index 00000000..a1f4b673
Binary files /dev/null and b/docs/assets/fonts/small-social/small-social.eot differ
diff --git a/docs/assets/fonts/small-social/small-social.svg b/docs/assets/fonts/small-social/small-social.svg
new file mode 100644
index 00000000..4275994c
--- /dev/null
+++ b/docs/assets/fonts/small-social/small-social.svg
@@ -0,0 +1,12 @@
+
+
+
diff --git a/docs/assets/fonts/small-social/small-social.ttf b/docs/assets/fonts/small-social/small-social.ttf
new file mode 100644
index 00000000..eb2e1bbd
Binary files /dev/null and b/docs/assets/fonts/small-social/small-social.ttf differ
diff --git a/docs/assets/fonts/small-social/small-social.woff b/docs/assets/fonts/small-social/small-social.woff
new file mode 100644
index 00000000..6671c4da
Binary files /dev/null and b/docs/assets/fonts/small-social/small-social.woff differ
diff --git a/docs/assets/ghspa.js b/docs/assets/ghspa.js
new file mode 100644
index 00000000..225b4811
--- /dev/null
+++ b/docs/assets/ghspa.js
@@ -0,0 +1,54 @@
+/**
+ *
+ * ____ _ ___ _ _ _ _ ___ ___ ____ ____ ____ ____ ____ ___ ____
+ * | __ | | |__| | | |__] |__] |__| | __ |___ [__ [__ |__] |__|
+ * |__] | | | | |__| |__] | | | |__] |___ ___] ___] | | |
+ *
+ * Easy way to enable Single Page Applications for GitHub Pages
+ *
+ * This project was released under MIT license.
+ *
+ * @link https://github.com/rafrex/spa-github-pages
+ * @author Rafael Pedicini
+ * @link http://websemantics.ca
+ * @author Adnan M.Sagar, PhD.
+ *
+ * @param {Object} l, the document current location
+ * @param {Boolean} projectPages, true by default, https://help.github.com/articles/user-organization-and-project-pages
+ *
+ */
+
+;(function(l, projectPages) {
+
+ var repo = projectPages ? '/' + l.pathname.split('/')[1] : '';
+
+ /* redirect all 404 trafic to index.html */
+ function redirect() {
+ l.replace(l.protocol + '//' + l.hostname + (l.port ? ':' + l.port : '') + repo + '/?' +
+ (l.pathname ? 'p=' + l.pathname.replace(/&/g, '~and~').replace(repo, '') : '') +
+ (l.search ? '&q=' + l.search.slice(1).replace(/&/g, '~and~') : '') +
+ (l.hash))
+ }
+
+ /* resolve 404 redirects into internal routes */
+ function resolve() {
+ if (l.search) {
+ var q = {};
+ l.search.slice(1).split('&').forEach(function(v) {
+ var a = v.split('=');
+ q[a[0]] = a.slice(1).join('=').replace(/~and~/g, '&')
+ });
+ if (q.p !== undefined) {
+ window.history.replaceState(null, null,
+ repo + (q.p || '') +
+ (q.q ? ('?' + q.q) : '') +
+ l.hash
+ )
+ }
+ }
+ }
+
+ /* if current document is 404 page page, redirect to index.html otherwise resolve */
+ document.title === '404' ? redirect() : resolve()
+
+}(window.location, window.projectPages || true ));
diff --git a/docs/assets/img/akveo-logo.png b/docs/assets/img/akveo-logo.png
new file mode 100755
index 00000000..a989be15
Binary files /dev/null and b/docs/assets/img/akveo-logo.png differ
diff --git a/docs/assets/img/avatars/1.png b/docs/assets/img/avatars/1.png
new file mode 100644
index 00000000..d66c5674
Binary files /dev/null and b/docs/assets/img/avatars/1.png differ
diff --git a/docs/assets/img/avatars/2.png b/docs/assets/img/avatars/2.png
new file mode 100644
index 00000000..b5443b76
Binary files /dev/null and b/docs/assets/img/avatars/2.png differ
diff --git a/docs/assets/img/avatars/3.png b/docs/assets/img/avatars/3.png
new file mode 100644
index 00000000..467d0bb2
Binary files /dev/null and b/docs/assets/img/avatars/3.png differ
diff --git a/docs/assets/img/avatars/4.png b/docs/assets/img/avatars/4.png
new file mode 100644
index 00000000..9e548a6a
Binary files /dev/null and b/docs/assets/img/avatars/4.png differ
diff --git a/docs/assets/img/avatars/5.png b/docs/assets/img/avatars/5.png
new file mode 100644
index 00000000..301879b2
Binary files /dev/null and b/docs/assets/img/avatars/5.png differ
diff --git a/docs/assets/img/avatars/6.png b/docs/assets/img/avatars/6.png
new file mode 100644
index 00000000..be57eff3
Binary files /dev/null and b/docs/assets/img/avatars/6.png differ
diff --git a/docs/assets/img/bakery-banner.png b/docs/assets/img/bakery-banner.png
new file mode 100644
index 00000000..e176b843
Binary files /dev/null and b/docs/assets/img/bakery-banner.png differ
diff --git a/docs/assets/img/bundle-dot-net-core.png b/docs/assets/img/bundle-dot-net-core.png
new file mode 100644
index 00000000..e7439040
Binary files /dev/null and b/docs/assets/img/bundle-dot-net-core.png differ
diff --git a/docs/assets/img/bundle-dot-net.png b/docs/assets/img/bundle-dot-net.png
new file mode 100644
index 00000000..c740ce9a
Binary files /dev/null and b/docs/assets/img/bundle-dot-net.png differ
diff --git a/docs/assets/img/bundle-node-js.png b/docs/assets/img/bundle-node-js.png
new file mode 100644
index 00000000..d6af6c47
Binary files /dev/null and b/docs/assets/img/bundle-node-js.png differ
diff --git a/docs/assets/img/bundle-scheme@1x.png b/docs/assets/img/bundle-scheme@1x.png
new file mode 100644
index 00000000..7aa003e6
Binary files /dev/null and b/docs/assets/img/bundle-scheme@1x.png differ
diff --git a/docs/assets/img/bundle-scheme@1x.webp b/docs/assets/img/bundle-scheme@1x.webp
new file mode 100644
index 00000000..53367597
Binary files /dev/null and b/docs/assets/img/bundle-scheme@1x.webp differ
diff --git a/docs/assets/img/bundle-scheme@2x.png b/docs/assets/img/bundle-scheme@2x.png
new file mode 100644
index 00000000..c3011a52
Binary files /dev/null and b/docs/assets/img/bundle-scheme@2x.png differ
diff --git a/docs/assets/img/bundle-scheme@2x.webp b/docs/assets/img/bundle-scheme@2x.webp
new file mode 100644
index 00000000..9b768595
Binary files /dev/null and b/docs/assets/img/bundle-scheme@2x.webp differ
diff --git a/docs/assets/img/corporate-theme.png b/docs/assets/img/corporate-theme.png
new file mode 100644
index 00000000..74fb2cf9
Binary files /dev/null and b/docs/assets/img/corporate-theme.png differ
diff --git a/docs/assets/img/cosmic-theme.png b/docs/assets/img/cosmic-theme.png
new file mode 100644
index 00000000..1d7607f9
Binary files /dev/null and b/docs/assets/img/cosmic-theme.png differ
diff --git a/docs/assets/img/dark-theme.png b/docs/assets/img/dark-theme.png
new file mode 100644
index 00000000..2af97aab
Binary files /dev/null and b/docs/assets/img/dark-theme.png differ
diff --git a/docs/assets/img/default.png b/docs/assets/img/default.png
new file mode 100644
index 00000000..f227640b
Binary files /dev/null and b/docs/assets/img/default.png differ
diff --git a/docs/assets/img/eva-icons.png b/docs/assets/img/eva-icons.png
new file mode 100755
index 00000000..5e2f2c9c
Binary files /dev/null and b/docs/assets/img/eva-icons.png differ
diff --git a/docs/assets/img/fleet-management-banner.jpg b/docs/assets/img/fleet-management-banner.jpg
new file mode 100644
index 00000000..c9fd7a6b
Binary files /dev/null and b/docs/assets/img/fleet-management-banner.jpg differ
diff --git a/docs/assets/img/fleet-management-banner.webp b/docs/assets/img/fleet-management-banner.webp
new file mode 100644
index 00000000..5447ba17
Binary files /dev/null and b/docs/assets/img/fleet-management-banner.webp differ
diff --git a/docs/assets/img/light-theme.png b/docs/assets/img/light-theme.png
new file mode 100644
index 00000000..035fd468
Binary files /dev/null and b/docs/assets/img/light-theme.png differ
diff --git a/docs/assets/img/logo.jpg b/docs/assets/img/logo.jpg
new file mode 100644
index 00000000..e6b5c3af
Binary files /dev/null and b/docs/assets/img/logo.jpg differ
diff --git a/docs/assets/img/material-dark-theme.png b/docs/assets/img/material-dark-theme.png
new file mode 100644
index 00000000..be144f9d
Binary files /dev/null and b/docs/assets/img/material-dark-theme.png differ
diff --git a/docs/assets/img/material-light-theme.png b/docs/assets/img/material-light-theme.png
new file mode 100644
index 00000000..1de279cd
Binary files /dev/null and b/docs/assets/img/material-light-theme.png differ
diff --git a/docs/assets/img/nebular.png b/docs/assets/img/nebular.png
new file mode 100755
index 00000000..119db9e3
Binary files /dev/null and b/docs/assets/img/nebular.png differ
diff --git a/docs/assets/img/ngx-admin-material-dark.png b/docs/assets/img/ngx-admin-material-dark.png
new file mode 100644
index 00000000..93a5b0fb
Binary files /dev/null and b/docs/assets/img/ngx-admin-material-dark.png differ
diff --git a/docs/assets/img/ngx-admin-material.jpg b/docs/assets/img/ngx-admin-material.jpg
new file mode 100644
index 00000000..585c13c3
Binary files /dev/null and b/docs/assets/img/ngx-admin-material.jpg differ
diff --git a/docs/assets/img/ngx-admin.png b/docs/assets/img/ngx-admin.png
new file mode 100644
index 00000000..7a148168
Binary files /dev/null and b/docs/assets/img/ngx-admin.png differ
diff --git a/docs/assets/img/product-hunt-cat.png b/docs/assets/img/product-hunt-cat.png
new file mode 100644
index 00000000..e06b88c8
Binary files /dev/null and b/docs/assets/img/product-hunt-cat.png differ
diff --git a/docs/environments/environment.prod.ts b/docs/environments/environment.prod.ts
new file mode 100644
index 00000000..803f39ed
--- /dev/null
+++ b/docs/environments/environment.prod.ts
@@ -0,0 +1,14 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ */
+
+// The file contents for the current environment will overwrite these during build.
+// The build system defaults to the dev environment which uses `environment.ts`, but if you do
+// `ng build --env=prod` then `environment.prod.ts` will be used instead.
+// The list of which env maps to which file can be found in `.angular-cli.json`.
+
+export const environment = {
+ production: true,
+};
diff --git a/docs/environments/environment.ts b/docs/environments/environment.ts
new file mode 100644
index 00000000..53d82519
--- /dev/null
+++ b/docs/environments/environment.ts
@@ -0,0 +1,14 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ */
+
+// The file contents for the current environment will overwrite these during build.
+// The build system defaults to the dev environment which uses `environment.ts`, but if you do
+// `ng build --env=prod` then `environment.prod.ts` will be used instead.
+// The list of which env maps to which file can be found in `.angular-cli.json`.
+
+export const environment = {
+ production: false,
+};
diff --git a/docs/favicon.png b/docs/favicon.png
new file mode 100644
index 00000000..71d39773
Binary files /dev/null and b/docs/favicon.png differ
diff --git a/docs/google85b6c09017edfbcb.html b/docs/google85b6c09017edfbcb.html
new file mode 100644
index 00000000..527a331c
--- /dev/null
+++ b/docs/google85b6c09017edfbcb.html
@@ -0,0 +1 @@
+google-site-verification: google85b6c09017edfbcb.html
diff --git a/docs/index.html b/docs/index.html
new file mode 100644
index 00000000..04d3b506
--- /dev/null
+++ b/docs/index.html
@@ -0,0 +1,53 @@
+
+
+
+
+ The most popular admin dashboard based on Angular 9+ and Nebular.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/main.ts b/docs/main.ts
new file mode 100644
index 00000000..11f9f131
--- /dev/null
+++ b/docs/main.ts
@@ -0,0 +1,18 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ */
+
+import { enableProdMode } from '@angular/core';
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { AppModule } from './app/app.module';
+import { environment } from './environments/environment';
+
+if (environment.production) {
+ enableProdMode();
+}
+
+platformBrowserDynamic().bootstrapModule(AppModule);
+
diff --git a/docs/output.json b/docs/output.json
new file mode 100644
index 00000000..0967ef42
--- /dev/null
+++ b/docs/output.json
@@ -0,0 +1 @@
+{}
diff --git a/docs/polyfills.ts b/docs/polyfills.ts
new file mode 100644
index 00000000..f27bfcc4
--- /dev/null
+++ b/docs/polyfills.ts
@@ -0,0 +1,74 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ */
+
+/**
+ * This file includes polyfills needed by Angular and is loaded before the app.
+ * You can add your own extra polyfills to this file.
+ *
+ * This file is divided into 2 sections:
+ * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
+ * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
+ * file.
+ *
+ * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
+ * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
+ * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
+ *
+ * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
+ */
+
+/***************************************************************************************************
+ * BROWSER POLYFILLS
+ */
+
+/** IE9, IE10 and IE11 requires all of the following polyfills. **/
+import 'core-js/es6/symbol';
+import 'core-js/es6/object';
+import 'core-js/es6/function';
+import 'core-js/es6/parse-int';
+import 'core-js/es6/parse-float';
+import 'core-js/es6/number';
+import 'core-js/es6/math';
+import 'core-js/es6/string';
+import 'core-js/es6/date';
+import 'core-js/es6/array';
+import 'core-js/es6/regexp';
+import 'core-js/es6/map';
+import 'core-js/es6/set';
+
+/** IE10 and IE11 requires the following for NgClass support on SVG elements */
+// import 'classlist.js'; // Run `npm install --save classlist.js`.
+
+/** IE10 and IE11 requires the following to support `@angular/animation`. */
+// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
+
+
+/** Evergreen browsers require these. **/
+import 'core-js/es6/reflect';
+import 'core-js/es7/reflect';
+import 'core-js/es7/array';
+import 'core-js/es7/object';
+
+
+/** ALL Firefox browsers require the following to support `@angular/animation`. **/
+// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
+
+
+/***************************************************************************************************
+ * Zone JS is required by Angular itself.
+ */
+import 'zone.js/dist/zone'; // Included with Angular CLI.
+
+
+/***************************************************************************************************
+ * APPLICATION IMPORTS
+ */
+
+/**
+ * Date, currency, decimal and percent pipes.
+ * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10
+ */
+// import 'intl'; // Run `npm install --save intl`.
diff --git a/docs/structure-landing.ts b/docs/structure-landing.ts
new file mode 100644
index 00000000..49d4db64
--- /dev/null
+++ b/docs/structure-landing.ts
@@ -0,0 +1,6 @@
+export const structure = [
+ {
+ type: 'page',
+ name: 'Material',
+ },
+];
diff --git a/docs/structure.ts b/docs/structure.ts
new file mode 100644
index 00000000..eb172c40
--- /dev/null
+++ b/docs/structure.ts
@@ -0,0 +1,103 @@
+export const structure = [
+ {
+ type: 'section',
+ name: 'Getting Started',
+ children: [
+ {
+ type: 'page',
+ name: 'What is ngx-admin?',
+ title: 'Ngx-admin - documentation',
+ description: 'ngx-admin is a front-end admin dashboard template based on Angular 9+, Bootstrap 4+ and Nebular',
+ keywords: 'Ngx-admin features, Angular 9+ typescript, Bootstrap 4+ & SCSS, ngx-admin documentation',
+ children: [
+ {
+ type: 'block',
+ block: 'markdown',
+ source: 'index.md',
+ },
+ ],
+ },
+ {
+ type: 'page',
+ name: 'Installation Guidelines',
+ title: 'Ngx-admin - Guideline to install.',
+ description: 'A guideline to install ngx-admin on your machine: backend integration,' +
+ ' tools you need to be installed.',
+ keywords: 'Ngx-admin install tools, ngx-admin versions, ngx-admin install.',
+ children: [
+ {
+ type: 'block',
+ block: 'markdown',
+ source: 'install-starter-kit.md',
+ },
+ ],
+ },
+ {
+ type: 'page',
+ name: 'Server deployment',
+ title: 'Ngx-admin - Server deployment',
+ description: 'How to set up your web-server to better serve the application on Angular.',
+ keywords: 'Ngx-admin server, ngx-admin server deployment',
+ children: [
+ {
+ type: 'block',
+ block: 'markdown',
+ source: 'server-deployment.md',
+ },
+ ],
+ },
+ ],
+ },
+ {
+ type: 'section',
+ name: 'Guides',
+ children: [
+ {
+ type: 'page',
+ name: 'Theme System',
+ title: 'Ngx-admin - Theme System',
+ description: 'Theme System in is a set of rules we put into how SCSS files and variables are organized.' +
+ ' Theme Map | Component Variables | Built-in-Themes',
+ keywords: 'Nebular theme system, nebular components, nebular theme map, ngx-admin built-in-themes',
+ children: [
+ {
+ type: 'block',
+ block: 'markdown',
+ source: 'concept-theme-system.md',
+ },
+ ],
+ },
+ {
+ type: 'page',
+ name: 'Change Theme',
+ title: 'Ngx-admin - Change theme',
+ description: 'How to change the current theme in ngx-admin.' +
+ ' Switch from Cosmic to Default. Runtime Theme Switch.',
+ keywords: 'ngx-admin runtime theme switch, ngx-admin theme change',
+ children: [
+ {
+ type: 'block',
+ block: 'markdown',
+ source: 'theme-change.md',
+ },
+ ],
+ },
+ {
+ type: 'page',
+ name: 'Backend integration',
+ title: 'Ngx-admin - Backend integration',
+ description: 'Approaches of integration of ngx-admin application with backend API.' +
+ ' Integration with JSON REST server, angular-cli/webpack-dev-server setup.',
+ keywords: 'Ngx-admin backend integration, JSON REST server integration,' +
+ ' angular-cli/webpack-dev-server setup, ngx-admin production setup',
+ children: [
+ {
+ type: 'block',
+ block: 'markdown',
+ source: 'backend-integration.md',
+ },
+ ],
+ },
+ ],
+ },
+];
diff --git a/docs/test.ts b/docs/test.ts
new file mode 100644
index 00000000..9a6cc57d
--- /dev/null
+++ b/docs/test.ts
@@ -0,0 +1,39 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ */
+
+// This file is required by karma.conf.js and loads recursively all the .spec and framework files
+
+import 'zone.js/dist/long-stack-trace-zone';
+import 'zone.js/dist/proxy.js';
+import 'zone.js/dist/sync-test';
+import 'zone.js/dist/jasmine-patch';
+import 'zone.js/dist/async-test';
+import 'zone.js/dist/fake-async-test';
+import { getTestBed } from '@angular/core/testing';
+import {
+ BrowserDynamicTestingModule,
+ platformBrowserDynamicTesting,
+} from '@angular/platform-browser-dynamic/testing';
+
+// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
+declare var __karma__: any;
+declare var require: any;
+
+// Prevent Karma from running prematurely.
+__karma__.loaded = function () {
+};
+
+// First, initialize the Angular testing environment.
+getTestBed().initTestEnvironment(
+ BrowserDynamicTestingModule,
+ platformBrowserDynamicTesting(),
+);
+// Then we find all the tests.
+const context = require.context('./', true, /\.spec\.ts$/);
+// And load the modules.
+context.keys().map(context);
+// Finally, start Karma to run the tests.
+__karma__.positionStart();
diff --git a/docs/tsconfig.app.json b/docs/tsconfig.app.json
new file mode 100644
index 00000000..fb506e7e
--- /dev/null
+++ b/docs/tsconfig.app.json
@@ -0,0 +1,16 @@
+{
+ "extends": "../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../out-tsc/app",
+ "baseUrl": "."
+ },
+ "exclude": [
+ "test.ts",
+ "**/*.spec.ts",
+ "assets/**/*.ts",
+ "dist/**/*"
+ ],
+ "include": [
+ "../docs/**/*"
+ ]
+}
diff --git a/docs/tsconfig.spec.json b/docs/tsconfig.spec.json
new file mode 100644
index 00000000..4ee370e5
--- /dev/null
+++ b/docs/tsconfig.spec.json
@@ -0,0 +1,27 @@
+{
+ "compilerOptions": {
+ "sourceMap": true,
+ "declaration": false,
+ "moduleResolution": "node",
+ "emitDecoratorMetadata": true,
+ "experimentalDecorators": true,
+ "lib": [
+ "es2017"
+ ],
+ "outDir": "../out-tsc/spec",
+ "module": "commonjs",
+ "target": "es6",
+ "baseUrl": "",
+ "types": [
+ "jasmine",
+ "node"
+ ]
+ },
+ "files": [
+ "test.ts",
+ "polyfills.ts"
+ ],
+ "include": [
+ "**/*.spec.ts"
+ ]
+}
diff --git a/docs/typings.d.ts b/docs/typings.d.ts
new file mode 100644
index 00000000..ae3e099a
--- /dev/null
+++ b/docs/typings.d.ts
@@ -0,0 +1,16 @@
+/**
+ * @license
+ * Copyright Akveo. All Rights Reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ */
+
+/* SystemJS module definition */
+declare var module: NodeModule;
+interface NodeModule {
+ id: string;
+}
+
+declare var require: NodeRequire;
+declare var structure: any;
+declare var docs: any;
+declare var hbspt: any;
diff --git a/docs/versions.json b/docs/versions.json
new file mode 100644
index 00000000..e3efffb8
--- /dev/null
+++ b/docs/versions.json
@@ -0,0 +1,9 @@
+[
+ {
+ "checkoutTarget": "demo",
+ "name": "demo",
+ "path": "/ngx-admin/",
+ "isCurrent": true
+ }
+]
+
diff --git a/gulpfile.js b/gulpfile.js
new file mode 100644
index 00000000..fee160df
--- /dev/null
+++ b/gulpfile.js
@@ -0,0 +1,12 @@
+'use strict';
+/**
+ * Load the TypeScript compiler and then load the tasks from 'scripts/gulp'.
+ */
+const path = require('path');
+const gulpPath = path.join(__dirname, 'scripts/gulp');
+const tsconfigPath = path.join(gulpPath, 'tsconfig.json');
+const tsconfig = require(tsconfigPath);
+
+// Register TypeScript.
+require('ts-node').register({ project: tsconfigPath });
+require(path.join(gulpPath, 'gulpfile'));
diff --git a/package.json b/package.json
index 22b01b26..a3328617 100644
--- a/package.json
+++ b/package.json
@@ -11,10 +11,13 @@
},
"scripts": {
"ng": "ng",
+ "ngh": "ngh",
+ "gulp": "gulp",
"conventional-changelog": "conventional-changelog",
"start": "ng serve",
"build": "ng build",
"build:prod": "npm run build -- --configuration production --aot",
+ "build:demo:prod": "npm run build -- --prod --aot --base-href /ngx-admin/",
"test": "ng test",
"test:coverage": "rimraf coverage && npm run test -- --code-coverage",
"lint": "ng lint",
@@ -24,7 +27,11 @@
"pree2e": "webdriver-manager update --standalone false --gecko false",
"e2e": "ng e2e",
"docs": "compodoc -p src/tsconfig.app.json -d docs",
- "docs:serve": "compodoc -p src/tsconfig.app.json -d docs -s",
+ "docs:dirs": "gulp create-docs-dirs",
+ "docs:prod": "npm run build -- docs --prod --aot --base-href /ngx-admin/",
+ "docs:build": "npm-run-all docs:prod docs:dirs",
+ "docs:serve": "npm start -- docs --port 4100",
+ "docs:gh-pages": "ts-node -P ./scripts/docs/tsconfig.json ./scripts/docs/build-docs.ts",
"prepush": "npm run lint:ci",
"release:changelog": "npm run conventional-changelog -- -p angular -i CHANGELOG.md -s",
"postinstall": "ngcc --properties es2015 es5 browser module main --first-only --create-ivy-entry-points --tsconfig \"./src/tsconfig.app.json\""
@@ -48,21 +55,28 @@
"@nebular/theme": "11.0.1",
"@swimlane/ngx-charts": "^14.0.0",
"angular2-chartjs": "0.4.1",
- "bootstrap": "4.3.1",
+ "bootstrap": "^4.4.1",
"chart.js": "2.7.1",
"ckeditor": "4.7.3",
"classlist.js": "1.1.20150312",
+ "colors.js": "1.2.4",
"core-js": "2.5.1",
"echarts": "^4.9.0",
"eva-icons": "^1.1.3",
+ "gulp-bump": "2.7.0",
+ "highlight.js": "^9.18.1",
"intl": "1.2.5",
"ionicons": "2.0.1",
"leaflet": "1.2.0",
+ "marked": "^0.5.2",
"nebular-icons": "1.1.0",
"ng2-ckeditor": "~1.2.9",
"ng2-completer": "^9.0.1",
+ "ng-inline-svg": "^11.0.0",
+ "ng-lazyload-image": "^7.1.0",
"ng2-smart-table": "^1.6.0",
"ngx-echarts": "^4.2.2",
+ "ngx-swiper-wrapper": "^9.0.1",
"normalize.css": "6.0.0",
"pace-js": "1.0.2",
"roboto-fontface": "0.8.0",
@@ -89,14 +103,23 @@
"@compodoc/compodoc": "1.0.1",
"@fortawesome/fontawesome-free": "^5.2.0",
"@types/d3-color": "1.0.5",
- "@types/jasmine": "~3.3.0",
+ "@types/gulp": "4.0.6",
+ "@types/jasmine": "^3.3.0",
"@types/jasminewd2": "2.0.3",
"@types/leaflet": "1.2.3",
"@types/node": "^12.12.70",
"@typescript-eslint/eslint-plugin": "^5.43.0",
"@typescript-eslint/parser": "^5.43.0",
"codelyzer": "^6.0.2",
+ "angular-cli-ghpages": "^0.6.2",
"conventional-changelog-cli": "1.3.4",
+ "doc-prsr": "2.2.0",
+ "gulp": "4.0.2",
+ "gulp-rename": "1.4.0",
+ "gulp-replace": "1.0.0",
+ "gulp-rollup": "2.13.0",
+ "gulp-sourcemaps": "2.6.5",
+ "gulp-typedoc": "2.2.3",
"eslint": "^8.28.0",
"husky": "0.13.3",
"jasmine-core": "~3.6.0",
@@ -110,10 +133,12 @@
"npm-run-all": "4.0.2",
"protractor": "~7.0.0",
"rimraf": "2.6.1",
+ "style-loader": "^1.1.3",
"stylelint": "7.13.0",
"ts-node": "3.2.2",
"tslint": "~6.1.0",
"tslint-language-service": "^0.9.9",
+ "typedoc": "^0.12.0",
"typescript": "~4.9.5"
}
}
diff --git a/scripts/docs/build-docs.ts b/scripts/docs/build-docs.ts
new file mode 100644
index 00000000..42365ce2
--- /dev/null
+++ b/scripts/docs/build-docs.ts
@@ -0,0 +1,124 @@
+import { join } from 'path';
+import { copy, mkdirp, remove, outputFile, writeJson, readJson } from 'fs-extra';
+
+import { generateGithubSpaScript } from './ghspa-template';
+import { runCommand } from './run-command';
+import { log } from './log';
+
+import { REPO_URL, OUT_DIR, REPO_OWNER, REPO_NAME } from './config';
+const WORK_DIR = join(process.cwd(), '../_DOCS_BUILD_WORK_DIR_');
+const MASTER_BRANCH_DIR = join(WORK_DIR, 'MASTER');
+const DOCS_VERSIONS_PATH = join(MASTER_BRANCH_DIR, 'docs/versions.json');
+
+export interface Version {
+ checkoutTarget: string;
+ name: string;
+ path: string;
+ isCurrent?: boolean;
+}
+
+(async function () {
+ log(`Cleaning work dir "${WORK_DIR}"`);
+ await remove(WORK_DIR);
+ log(`Cleaning output dir "${OUT_DIR}"`);
+ await remove(OUT_DIR);
+
+ log(`Creating work dir "${WORK_DIR}"`);
+ await mkdirp(WORK_DIR);
+
+ log(`Cloning ${REPO_URL} into ${MASTER_BRANCH_DIR}`);
+ await runCommand(`git clone -b demo ${REPO_URL} ${MASTER_BRANCH_DIR}`, { cwd: WORK_DIR });
+
+ log('Reading versions configuration');
+ const config: Version[] = await import(DOCS_VERSIONS_PATH);
+ ensureSingleCurrentVersion(config);
+
+ log(`Versions configuration:`);
+ const jsonConfig = JSON.stringify(config, null, ' ');
+ log(jsonConfig);
+
+ log(`Building docs`);
+ await buildDocs(config);
+
+ log(`Adding versions.json to ${OUT_DIR}`);
+ await outputFile(join(OUT_DIR, 'versions.json'), jsonConfig);
+
+ log(`Deploying to ghpages`);
+ await deploy(OUT_DIR);
+
+ log(`Cleaning up working directory (${WORK_DIR})`);
+ await remove(WORK_DIR);
+}());
+
+function ensureSingleCurrentVersion(versions: Version[]) {
+ const currentVersion = versions.filter(v => v.isCurrent);
+ if (currentVersion.length !== 1) {
+ throw new Error(`Versions config error: Only one current version allowed.`)
+ }
+}
+
+async function buildDocs(versions: Version[]) {
+ const ghspaScript = generateGithubSpaScript(versions);
+
+ return Promise.all(versions.map((version: Version) => {
+ const versionDistDir = version.isCurrent
+ ? OUT_DIR
+ : join(OUT_DIR, version.name);
+
+ return prepareVersion(version, versionDistDir, ghspaScript);
+ }));
+}
+
+async function prepareVersion(version: Version, distDir: string, ghspaScript: string) {
+ const projectDir = join(WORK_DIR, `${version.name}`);
+
+ await copyToBuildDir(MASTER_BRANCH_DIR, projectDir);
+ await checkoutVersion(version.checkoutTarget, projectDir);
+ await runCommand('npm install', { cwd: projectDir });
+ await addVersionNameToPackageJson(version.name, join(projectDir, 'package.json'));
+ await buildDocsApp(projectDir, version.path);
+ await copy(join(projectDir, 'docs/dist'), distDir);
+ await outputFile(join(distDir, 'assets/ghspa.js'), ghspaScript);
+
+ await remove(projectDir);
+}
+
+async function copyToBuildDir(from: string, to: string) {
+ try {
+ await mkdirp(to);
+ await copy(from, to);
+ } catch (e) {
+ throw new Error(`Error copying from ${from} to ${to}: ${e.message}`);
+ }
+}
+
+async function checkoutVersion(checkoutTarget: string, repoDirectory: string) {
+ try {
+ await runCommand(`git checkout ${checkoutTarget}`, { cwd: repoDirectory, showLog: false });
+ } catch (e) {
+ throw new Error(`Error checking out ${checkoutTarget}: ${e.message}`);
+ }
+}
+
+async function addVersionNameToPackageJson(versionName: string, packageJsonPath: string) {
+ const packageJsonObject = await readJson(packageJsonPath);
+ packageJsonObject.versionName = versionName;
+ await writeJson(packageJsonPath, packageJsonObject);
+}
+
+async function buildDocsApp(projectDir: string, baseHref: string) {
+ if (!baseHref.endsWith('/')) {
+ baseHref = baseHref + '/';
+ }
+
+ await runCommand(`npm run build -- docs --prod --base-href ${baseHref}`, { cwd: projectDir });
+ await runCommand('npm run docs:dirs', { cwd: projectDir });
+ }
+
+async function deploy(distDir: string) {
+ await runCommand(
+ `npx angular-cli-ghpages -S --dir . --repo=https://GH_TOKEN@github.com/${REPO_OWNER}/${REPO_NAME}.git`,
+ { cwd: distDir, showLog: true },
+ );
+}
+
diff --git a/scripts/docs/config.ts b/scripts/docs/config.ts
new file mode 100644
index 00000000..92498fc5
--- /dev/null
+++ b/scripts/docs/config.ts
@@ -0,0 +1,4 @@
+export const REPO_URL = 'https://github.com/akveo/ngx-admin.git';
+export const REPO_OWNER = 'akveo';
+export const REPO_NAME = 'ngx-admin';
+export const OUT_DIR = 'docs/dist'; // Relative to the directory you run command
diff --git a/scripts/docs/ghspa-template.ts b/scripts/docs/ghspa-template.ts
new file mode 100644
index 00000000..0ca05003
--- /dev/null
+++ b/scripts/docs/ghspa-template.ts
@@ -0,0 +1,78 @@
+import { Version } from './build-docs';
+
+function removeTrailingSlash(path: string): string {
+ if (path.endsWith('/')) {
+ return path.slice(0, -1);
+ }
+ return path;
+}
+
+export function generateGithubSpaScript(versionsToRedirect: Version[]): string {
+ const paths: string[] = versionsToRedirect.map(v => v.path).map(removeTrailingSlash);
+
+ return `
+/**
+ *
+ * ____ _ ___ _ _ _ _ ___ ___ ____ ____ ____ ____ ____ ___ ____
+ * | __ | | |__| | | |__] |__] |__| | __ |___ [__ [__ |__] |__|
+ * |__] | | | | |__| |__] | | | |__] |___ ___] ___] | | |
+ *
+ * Easy way to enable Single Page Applications for GitHub Pages
+ *
+ * This project was released under MIT license.
+ *
+ * @link https://github.com/rafrex/spa-github-pages
+ * @author Rafael Pedicini
+ * @link http://websemantics.ca
+ * @author Adnan M.Sagar, PhD.
+ *
+ * @param {Object} l, the document current location
+ * @param {Boolean} projectPages, true by default, https://help.github.com/articles/user-organization-and-project-pages
+ *
+ */
+
+;(function(l) {
+
+ var redirectPath;
+ ${JSON.stringify(paths)}.forEach(function (path) {
+ if (l.pathname.indexOf(path) === 0) {
+ redirectPath = path;
+ }
+ });
+
+ if (!redirectPath) {
+ return;
+ }
+
+ /* redirect all 404 traffic to index.html */
+ function redirect() {
+ l.replace(l.protocol + '//' + l.hostname + (l.port ? ':' + l.port : '') + redirectPath + '/?' +
+ (l.pathname ? 'p=' + l.pathname.replace(/&/g, '~and~').replace(redirectPath, '') : '') +
+ (l.search ? '&q=' + l.search.slice(1).replace(/&/g, '~and~') : '') +
+ (l.hash))
+ }
+
+ /* resolve 404 redirects into internal routes */
+ function resolve() {
+ if (l.search) {
+ var q = {};
+ l.search.slice(1).split('&').forEach(function(v) {
+ var a = v.split('=');
+ q[a[0]] = a.slice(1).join('=').replace(/~and~/g, '&')
+ });
+ if (q.p !== undefined) {
+ window.history.replaceState(null, null,
+ redirectPath + (q.p || '') +
+ (q.q ? ('?' + q.q) : '') +
+ l.hash
+ )
+ }
+ }
+ }
+
+ /* if current document is 404 page page, redirect to index.html otherwise resolve */
+ document.title === '404' ? redirect() : resolve()
+
+}(window.location));
+`;
+}
diff --git a/scripts/docs/log.ts b/scripts/docs/log.ts
new file mode 100644
index 00000000..bcef87a7
--- /dev/null
+++ b/scripts/docs/log.ts
@@ -0,0 +1,3 @@
+export function log(message: string) {
+ console.log(message);
+}
diff --git a/scripts/docs/run-command.ts b/scripts/docs/run-command.ts
new file mode 100644
index 00000000..bcd7df42
--- /dev/null
+++ b/scripts/docs/run-command.ts
@@ -0,0 +1,37 @@
+import { exec } from 'child_process';
+import { promisify } from 'util';
+
+export interface RunCommandOptions {
+ cwd?: string;
+ showLog?: boolean;
+}
+
+const DEFAULT_OPTIONS: RunCommandOptions = { cwd: process.cwd(), showLog: false };
+
+export async function runCommand(command: string, options?: RunCommandOptions) {
+ let { cwd, showLog } = Object.assign({}, DEFAULT_OPTIONS, options);
+
+ try {
+ console.log(`Running "${command}" in "${cwd}"`);
+ const { stdout, stderr } = await promisify(exec)(command, { cwd });
+
+ if (showLog && stdout) {
+ console.log(stdout);
+ }
+
+ if (stderr) {
+ console.log(`stderr from "${command}" in "${cwd}"`);
+ console.warn(stderr);
+ }
+ } catch ({ message, stdout, stderr }) {
+ let errorMessage = `Error running "${command}" in "${cwd}": ${message}.`;
+ if (stdout) {
+ errorMessage += `\nstdout: ${stdout}`;
+ console.error(stdout);
+ }
+ if (stderr) {
+ errorMessage += `\nstderr: ${stderr}`;
+ }
+ throw new Error(errorMessage);
+ }
+}
diff --git a/scripts/docs/tsconfig.json b/scripts/docs/tsconfig.json
new file mode 100644
index 00000000..ad976e84
--- /dev/null
+++ b/scripts/docs/tsconfig.json
@@ -0,0 +1,12 @@
+{
+ "compilerOptions": {
+ "rootDir": ".",
+ "module": "commonjs",
+ "lib": ["es2018"],
+ "target": "es2018",
+ "resolveJsonModule": true
+ },
+ "include": [
+ "build-docs.ts"
+ ]
+}
diff --git a/scripts/docs/tslint.json b/scripts/docs/tslint.json
new file mode 100644
index 00000000..e3929e28
--- /dev/null
+++ b/scripts/docs/tslint.json
@@ -0,0 +1,5 @@
+{
+ "rules": {
+ "no-console": false
+ }
+}
diff --git a/scripts/gulp/gulpfile.ts b/scripts/gulp/gulpfile.ts
new file mode 100644
index 00000000..88104c7d
--- /dev/null
+++ b/scripts/gulp/gulpfile.ts
@@ -0,0 +1 @@
+import './tasks/docs/docs';
diff --git a/scripts/gulp/tasks/config.ts b/scripts/gulp/tasks/config.ts
new file mode 100644
index 00000000..2fb538c6
--- /dev/null
+++ b/scripts/gulp/tasks/config.ts
@@ -0,0 +1 @@
+export const DOCS_DIST = './docs/dist';
diff --git a/scripts/gulp/tasks/docs/docs.ts b/scripts/gulp/tasks/docs/docs.ts
new file mode 100644
index 00000000..69e74f9e
--- /dev/null
+++ b/scripts/gulp/tasks/docs/docs.ts
@@ -0,0 +1,85 @@
+import { task, series } from 'gulp';
+import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
+import { isAbsolute, join, resolve, sep } from 'path';
+
+import { structure as DOCS } from '../../../../docs/structure';
+import { structure as LANDING } from '../../../../docs/structure-landing';
+import { DOCS_DIST } from '../config';
+
+task('create-docs-dirs', (done) => {
+ const docsStructure = flatten('docs', routesTree(DOCS));
+ createDirsStructure(docsStructure);
+
+ const landingStructure = flatten('', routesTree(LANDING));
+ createDirsStructure(landingStructure);
+
+ done();
+});
+
+function routesTree(structure) {
+ return structure
+ .filter((page: any) => ['section', 'page', 'tabs'].includes(page.type))
+ .map((page: any) => {
+ if (page.type === 'tabs') {
+ page.children = ['overview', 'api', 'theme', 'examples']
+ .map(name => ({ name, type: 'page'}));
+ }
+ return page;
+ })
+ .map((page: any) => {
+ return {
+ path: prepareSlag(page.name),
+ children: page.children ? routesTree(page.children) : [],
+ }
+ });
+}
+
+function prepareSlag(name) {
+ return name.replace(/[^a-zA-Z0-9\s]+/g, '')
+ .replace(/\s/g, '-')
+ .toLowerCase();
+}
+
+function flatten(root, arr) {
+ let res: any[] = [];
+ arr.forEach((item: any) => {
+ const path = `${root}/${item.path}`;
+ res.push(path);
+ if (item.children) {
+ res = res.concat(flatten(path, item.children));
+ }
+ });
+
+ return res;
+}
+
+function createDirsStructure(dirs) {
+ const index = readFileSync(join(DOCS_DIST, 'index.html'), 'utf8');
+ dirs.forEach((dir: any) => {
+
+ const fullPath = join(DOCS_DIST, dir);
+ if (!existsSync(fullPath)) {
+ mkDirByPathSync(fullPath);
+ }
+
+ writeFileSync(join(fullPath, 'index.html'), index);
+ });
+}
+
+function mkDirByPathSync(targetDir, {isRelativeToScript = false} = {}) {
+ const initDir = isAbsolute(targetDir) ? sep : '';
+ const baseDir = isRelativeToScript ? __dirname : '.';
+
+ targetDir.split(sep).reduce((parentDir, childDir) => {
+ const curDir = resolve(baseDir, parentDir, childDir);
+ try {
+ mkdirSync(curDir);
+ } catch (err) {
+ if (err.code !== 'EEXIST') {
+ throw err;
+ }
+ }
+
+ return curDir;
+ }, initDir);
+}
diff --git a/scripts/gulp/tsconfig.json b/scripts/gulp/tsconfig.json
new file mode 100644
index 00000000..522c3fea
--- /dev/null
+++ b/scripts/gulp/tsconfig.json
@@ -0,0 +1,23 @@
+{
+ "compilerOptions": {
+ "experimentalDecorators": true,
+ "noUnusedParameters": true,
+ "lib": [
+ "es2017"
+ ],
+ "module": "commonjs",
+ "moduleResolution": "node",
+ "strictNullChecks": true,
+ "target": "es5",
+ "typeRoots": [
+ "node_modules/@types"
+ ],
+ "types": [
+ "node"
+ ],
+ "baseUrl": "."
+ },
+ "files": [
+ "gulpfile.ts"
+ ]
+}
diff --git a/src/app/@core/core.module.ts b/src/app/@core/core.module.ts
index 26bf0743..4f3f8188 100644
--- a/src/app/@core/core.module.ts
+++ b/src/app/@core/core.module.ts
@@ -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({
diff --git a/src/app/@core/guard/theme.guard.ts b/src/app/@core/guard/theme.guard.ts
new file mode 100644
index 00000000..9c3f6671
--- /dev/null
+++ b/src/app/@core/guard/theme.guard.ts
@@ -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 | Promise | 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;
+ }
+}
diff --git a/src/app/@core/utils/ab.service.ts b/src/app/@core/utils/ab.service.ts
new file mode 100644
index 00000000..6091f39a
--- /dev/null
+++ b/src/app/@core/utils/ab.service.ts
@@ -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(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),
+ );
+ }
+}
diff --git a/src/app/@core/utils/analytics.service.ts b/src/app/@core/utils/analytics.service.ts
index 20dcc02c..e9a14d6d 100644
--- a/src/app/@core/utils/analytics.service.ts
+++ b/src/app/@core/utils/analytics.service.ts
@@ -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);
+ }
}
diff --git a/src/app/@core/utils/metadata.service.ts b/src/app/@core/utils/metadata.service.ts
new file mode 100644
index 00000000..79d85984
--- /dev/null
+++ b/src/app/@core/utils/metadata.service.ts
@@ -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 });
+ }
+
+}
diff --git a/src/app/@core/utils/theme.service.ts b/src/app/@core/utils/theme.service.ts
new file mode 100644
index 00000000..0ca26925
--- /dev/null
+++ b/src/app/@core/utils/theme.service.ts
@@ -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 = 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;
+ }
+}
diff --git a/src/app/@theme/components/call-action-card/call-action-card.component.scss b/src/app/@theme/components/call-action-card/call-action-card.component.scss
new file mode 100644
index 00000000..eaac4232
--- /dev/null
+++ b/src/app/@theme/components/call-action-card/call-action-card.component.scss
@@ -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;
+ }
+ }
+}
diff --git a/src/app/@theme/components/call-action-card/call-action-card.component.ts b/src/app/@theme/components/call-action-card/call-action-card.component.ts
new file mode 100644
index 00000000..2418913c
--- /dev/null
+++ b/src/app/@theme/components/call-action-card/call-action-card.component.ts
@@ -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: `
+
+