Compare commits

...

50 Commits

Author SHA1 Message Date
sercan
56c4eb0356 Updated changelog 2021-07-01 14:26:49 +03:00
sercan
a8a4f2b18d Updated changelog and increased the version number 2021-07-01 13:31:41 +03:00
sercan
36c8727616 (layout/settings-drawer) Fixed: Issues on small screen devices 2021-07-01 12:55:02 +03:00
sercan
5ea37aed99 (layouts) Fixed: Header buttons are not fitting on certain layouts
(layouts) Hide the "fuse-fullscreen" button on smaller devices since they don't support fullscreen
2021-07-01 12:50:34 +03:00
sercan
cbce71f155 (apps/help-center) Fixed: Small dark mode issues 2021-07-01 12:37:27 +03:00
sercan
ed47050232 (apps/ecommerce/inventory) Fixed: Tags selector border colors are not correct on dark mode 2021-07-01 12:37:09 +03:00
sercan
c86a538d41 (Angular Material) Apply rounded styles by default 2021-07-01 12:23:49 +03:00
sercan
f369206ef8 (layout/common) Explicitly define the overlay position strategy properties
(apps/contacts) Explicitly define the overlay position strategy properties
(apps/mailbox) Explicitly define the overlay position strategy properties
(apps/tasks) Explicitly define the overlay position strategy properties
2021-06-25 22:57:14 +03:00
sercan
1659d4babd (tailwindcss) Fixed: Dark mode classes are not being purged correctly due to the wrong safelist entry 2021-06-25 12:21:34 +03:00
sercan
fa37f99d33 (docs) Use alternative spacing since 12s are removed 2021-06-25 11:09:30 +03:00
sercan
331968ac5b (tailwindcss) Breaking: Removed 5, 6 & 12 fractional spacing values since they are not used in Demo by default and they are mostly not needed because of Flex and Grid. If you happen to use them, you can manually add them back. 2021-06-25 11:08:56 +03:00
sercan
27274c84d6 (@fuse/navigation) Fixed: Vertical navigation blocks scroll if it's destroyed while in 'over' mode and opened 2021-06-24 18:04:19 +03:00
sercan
4b8a101a3e (transloco) Fixed: Language files cannot be loaded if using a base href other than "/" 2021-06-24 10:54:03 +03:00
sercan
8080a85d40 (fuse-drawer) Fixed: Memory leak due to the animation player, thanks to Vadym Pidoplichko for coming up the issue and the solution. 2021-06-24 10:29:34 +03:00
sercan
36784c405f (package.json) Added "description" and "author" fields 2021-06-21 18:56:15 +03:00
sercan
8f4f7886d5 (fuse/navigation) Moved *ngIf directives into their own "ng-container" containers 2021-06-20 21:59:24 +03:00
sercan
d693a08136 (general) Tweaked the "Angular Material components" navigation item title 2021-06-20 21:30:10 +03:00
sercan
0c0ef40de3 (docs) Added docs about navigation tooltip 2021-06-20 21:29:37 +03:00
sercan
f4d737d3a3 (fuse/navigation) Added "tooltip" property to show tooltips on navigation items using MatTooltip 2021-06-20 21:28:47 +03:00
sercan
47d9ddb08c Updated changelog 2021-06-15 08:32:13 +03:00
sercan
4da3612d22 Updated changelog and increased the version number 2021-06-14 14:29:58 +03:00
sercan
403a949d4a (apps/ecommerce/inventory) Small mobile experience tweaks 2021-06-14 12:25:04 +03:00
sercan
5b78a68116 (apps/ecommerce/inventory) Better image containment on row details 2021-06-14 12:22:51 +03:00
sercan
214116e10d (apps/ecommerce/inventory) Replaced the mat-table with a custom grid for better mobile experience and better performance, improved the mobile experience 2021-06-14 12:03:17 +03:00
sercan
5962c80e8d (docs) Added "target" docs to navigation 2021-06-14 08:53:01 +03:00
sercan
ff086b1ed0 (fuse/navigation) Added "target" for setting the target attribute on outgoing links. Thanks @danielehrhardt for the original PR, closes #154 2021-06-14 08:52:29 +03:00
sercan
efdfa6418a (core) Go back to the 'classy' layout by default 2021-06-10 14:56:21 +03:00
sercan
c1c9904b9d (layouts/common/search) Improved the autocomplete design 2021-06-10 14:56:00 +03:00
sercan
39650d3cc9 (layouts) Use navigation service data for horizontal navigation 2021-06-10 10:13:43 +03:00
sercan
bafa9adc01 (ui/page-layouts) Added tabbed version of Simple Fullwidth page layout 2021-06-09 11:41:39 +03:00
sercan
bb9023f9df (fuse/navigation) Fixed: First children of collapsable items don't have proper spacing at the top 2021-06-09 10:32:26 +03:00
sercan
66096718e0 (docs) Updated the docs to reflect the latest changes 2021-06-09 10:22:18 +03:00
sercan
945d0a2240 (fuse/fullscreen) Added [tooltip] & [iconTpl] inputs for customizing the Fullscreen trigger button 2021-06-08 18:29:49 +03:00
sercan
9005f08ac7 (app.resolver) Use services to request the initial data
(layouts) Common components of layouts now requests their data directly from their service rather than getting it from route data
(core) New navigation service to request and store the navigation data
2021-06-08 17:29:45 +03:00
sercan
89f5a4ec69 (core/user) Renamed user.model to user.types for better consistency 2021-06-07 12:18:39 +03:00
sercan
ca7b4c7e5d (ui/advanced-search) Removed unused MatIconModule import 2021-06-07 11:41:29 +03:00
sercan
bb57ec2324 (ui/advanced-search) Added an example search form that uses query parameters for Advanced Search forms 2021-06-07 11:40:33 +03:00
sercan
11ad2c89df (apps/file-manager) Added support for nested folder views 2021-06-05 13:57:39 +03:00
sercan
fc1e7b02b0 (data/navigation) Fixed the "Invoice" icon, added missing dashboard links to the "futuristicNavigation" data 2021-06-04 22:28:53 +03:00
sercan
446bfe4139 (ui/angular-material) Added a component list that redirects to the related page of the official docs 2021-06-04 22:21:47 +03:00
sercan
2c5cd60c0a (general) Updated version numbers and changelog 2021-06-03 13:25:07 +03:00
sercan
decb238f73 (dependencies) Updated various dependencies to their latest versions
(fuse/highlight) Updated the import path
2021-06-03 13:18:05 +03:00
sercan
ab3ad4fd2f (apps/scrumboard) Dark mode fixes and tweaks 2021-06-03 11:27:51 +03:00
sercan
c2dd77d7a3 (angular.json) Removed "e2e" entry, fixed the styles file path for "test" 2021-06-02 12:45:12 +03:00
sercan
a78b087a68 (apps/scrumboard) New version of the Scrumboard app 2021-06-02 11:42:22 +03:00
sercan
84d40427a1 (apps/ecommerce) Small tweaks
(apps/mailbox) Small tweaks
2021-06-02 11:41:47 +03:00
sercan
f295fd9061 (fuse/autogrow) BREAKING: Removed fuseAutogrow in favor of matTextareaAutosize since all of its problems solved, use [matTextareaAutosize] without any vertical padding on the textarea itself. 2021-05-31 17:14:03 +03:00
sercan
b98cfc1d37 (Angular Material) Increased default MatDialog border radius to 16px for better consistency 2021-05-31 16:12:08 +03:00
sercan
0ba5677c01 (Dependencies) Updated Angular & Angular Material to v12.0.2 2021-05-31 10:49:51 +03:00
sercan
96ef1281ae (Angular Material) Fixed: Density setting is not working for dark themes 2021-05-31 10:45:29 +03:00
223 changed files with 9353 additions and 4924 deletions

View File

@@ -120,7 +120,7 @@
"src/assets"
],
"styles": [
"src/styles.scss"
"src/styles/styles.scss"
],
"scripts": []
}
@@ -133,18 +133,6 @@
"src/**/*.html"
]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "fuse:serve"
},
"configurations": {
"production": {
"devServerTarget": "fuse:serve:production"
}
}
}
}
}

5165
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,8 @@
{
"name": "@fuse/demo",
"version": "13.0.2",
"version": "13.2.0",
"description": "Fuse - Angular Admin Template and Starter Project",
"author": "https://themeforest.net/user/srcn",
"license": "https://themeforest.net/licenses/standard",
"private": true,
"scripts": {
@@ -12,17 +14,17 @@
"e2e": "ng e2e"
},
"dependencies": {
"@angular/animations": "12.0.0",
"@angular/cdk": "12.0.0",
"@angular/common": "12.0.0",
"@angular/compiler": "12.0.0",
"@angular/core": "12.0.0",
"@angular/forms": "12.0.0",
"@angular/material": "12.0.0",
"@angular/material-moment-adapter": "12.0.0",
"@angular/platform-browser": "12.0.0",
"@angular/platform-browser-dynamic": "12.0.0",
"@angular/router": "12.0.0",
"@angular/animations": "12.1.1",
"@angular/cdk": "12.1.1",
"@angular/common": "12.1.1",
"@angular/compiler": "12.1.1",
"@angular/core": "12.1.1",
"@angular/forms": "12.1.1",
"@angular/material": "12.1.1",
"@angular/material-moment-adapter": "12.1.1",
"@angular/platform-browser": "12.1.1",
"@angular/platform-browser-dynamic": "12.1.1",
"@angular/router": "12.1.1",
"@fullcalendar/angular": "4.4.5-beta",
"@fullcalendar/core": "4.4.2",
"@fullcalendar/daygrid": "4.4.2",
@@ -31,62 +33,59 @@
"@fullcalendar/moment": "4.4.2",
"@fullcalendar/rrule": "4.4.2",
"@fullcalendar/timegrid": "4.4.2",
"@ngneat/transloco": "2.20.1",
"apexcharts": "3.26.3",
"@ngneat/transloco": "2.21.0",
"apexcharts": "3.27.1",
"crypto-js": "3.3.0",
"highlight.js": "10.7.2",
"highlight.js": "11.0.1",
"lodash-es": "4.17.21",
"moment": "2.29.1",
"ng-apexcharts": "1.5.10",
"ngx-markdown": "12.0.0",
"ngx-quill": "14.0.0",
"ng-apexcharts": "1.5.12",
"ngx-markdown": "12.0.1",
"ngx-quill": "14.1.1",
"perfect-scrollbar": "1.5.1",
"quill": "1.3.7",
"rrule": "2.6.8",
"rxjs": "6.6.7",
"tslib": "2.2.0",
"tslib": "2.3.0",
"web-animations-js": "2.3.2",
"zone.js": "0.11.4"
},
"devDependencies": {
"@angular-devkit/build-angular": "12.0.0",
"@angular-eslint/builder": "12.0.0",
"@angular-eslint/eslint-plugin": "12.0.0",
"@angular-eslint/eslint-plugin-template": "12.0.0",
"@angular-eslint/schematics": "12.0.0",
"@angular-eslint/template-parser": "12.0.0",
"@angular/cli": "12.0.0",
"@angular/compiler-cli": "12.0.0",
"@angular/language-service": "12.0.0",
"@tailwindcss/aspect-ratio": "0.2.0",
"@tailwindcss/line-clamp": "0.2.0",
"@tailwindcss/typography": "0.4.0",
"@angular-devkit/build-angular": "12.1.0",
"@angular-eslint/builder": "12.2.0",
"@angular-eslint/eslint-plugin": "12.2.0",
"@angular-eslint/eslint-plugin-template": "12.2.0",
"@angular-eslint/schematics": "12.2.0",
"@angular-eslint/template-parser": "12.2.0",
"@angular/cli": "12.1.0",
"@angular/compiler-cli": "12.1.1",
"@tailwindcss/aspect-ratio": "0.2.1",
"@tailwindcss/line-clamp": "0.2.1",
"@tailwindcss/typography": "0.4.1",
"@types/chroma-js": "2.1.3",
"@types/crypto-js": "3.1.47",
"@types/highlight.js": "10.1.0",
"@types/jasmine": "3.6.11",
"@types/lodash": "4.14.169",
"@types/lodash": "4.14.170",
"@types/lodash-es": "4.17.4",
"@types/node": "12.20.13",
"@typescript-eslint/eslint-plugin": "4.24.0",
"@typescript-eslint/parser": "4.24.0",
"autoprefixer": "10.2.5",
"@types/node": "12.20.15",
"@typescript-eslint/eslint-plugin": "4.28.1",
"@typescript-eslint/parser": "4.28.1",
"autoprefixer": "10.2.6",
"chroma-js": "2.1.2",
"eslint": "7.26.0",
"eslint-plugin-import": "2.23.2",
"eslint-plugin-jsdoc": "34.8.2",
"eslint": "7.29.0",
"eslint-plugin-import": "2.23.4",
"eslint-plugin-jsdoc": "35.4.1",
"eslint-plugin-prefer-arrow": "1.2.3",
"jasmine-core": "3.7.1",
"jasmine-spec-reporter": "5.0.2",
"karma": "6.3.2",
"karma": "6.3.4",
"karma-chrome-launcher": "3.1.0",
"karma-coverage": "2.0.3",
"karma-jasmine": "4.0.1",
"karma-jasmine-html-reporter": "1.6.0",
"lodash": "4.17.21",
"postcss": "8.3.0",
"protractor": "7.0.0",
"tailwindcss": "2.1.2",
"typescript": "4.2.4"
"postcss": "8.3.5",
"tailwindcss": "2.2.4",
"typescript": "4.3.5"
}
}

View File

@@ -341,6 +341,13 @@ export class FuseDrawerComponent implements OnChanges, OnInit, OnDestroy
animate('300ms cubic-bezier(0.25, 0.8, 0.25, 1)', style({opacity: 1}))
]).create(this._overlay);
// Once the animation is done...
this._player.onDone(() => {
// Destroy the player
this._player.destroy();
});
// Play the animation
this._player.play();
@@ -373,6 +380,9 @@ export class FuseDrawerComponent implements OnChanges, OnInit, OnDestroy
// Once the animation is done...
this._player.onDone(() => {
// Destroy the player
this._player.destroy();
// If the backdrop still exists...
if ( this._overlay )
{

View File

@@ -1,7 +1,12 @@
<!-- Button -->
<button
mat-icon-button
[matTooltip]="'Toggle Fullscreen'"
[matTooltip]="tooltip || 'Toggle Fullscreen'"
(click)="toggleFullscreen()">
<mat-icon [svgIcon]="'heroicons_outline:arrows-expand'"></mat-icon>
<ng-container [ngTemplateOutlet]="iconTpl || defaultIconTpl"></ng-container>
</button>
<!-- Default icon -->
<ng-template #defaultIconTpl>
<mat-icon [svgIcon]="'heroicons_outline:arrows-expand'"></mat-icon>
</ng-template>

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, Inject, OnInit, ViewEncapsulation } from '@angular/core';
import { ChangeDetectionStrategy, Component, Inject, Input, OnInit, TemplateRef, ViewEncapsulation } from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { FSDocument, FSDocumentElement } from '@fuse/components/fullscreen/fullscreen.types';
@@ -11,6 +11,8 @@ import { FSDocument, FSDocumentElement } from '@fuse/components/fullscreen/fulls
})
export class FuseFullscreenComponent implements OnInit
{
@Input() iconTpl: TemplateRef<any>;
@Input() tooltip: string;
private _fsDoc: FSDocument;
private _fsDocEl: FSDocumentElement;
private _isFullscreen: boolean = false;

View File

@@ -3,15 +3,17 @@ import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatTooltipModule } from '@angular/material/tooltip';
import { FuseFullscreenComponent } from '@fuse/components/fullscreen/fullscreen.component';
import { CommonModule } from '@angular/common';
@NgModule({
declarations: [
FuseFullscreenComponent
],
imports : [
imports: [
MatButtonModule,
MatIconModule,
MatTooltipModule
MatTooltipModule,
CommonModule
],
exports : [
FuseFullscreenComponent

View File

@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
import * as hljs from 'highlight.js';
import hljs from 'highlight.js';
@Injectable({
providedIn: 'root'

View File

@@ -5,69 +5,83 @@
[ngClass]="item.classes?.wrapper">
<!-- Item with an internal link -->
<div
class="fuse-horizontal-navigation-item"
*ngIf="item.link && !item.externalLink && !item.function && !item.disabled"
[ngClass]="{'fuse-horizontal-navigation-item-active-forced': item.active}"
[routerLink]="[item.link]"
[routerLinkActive]="'fuse-horizontal-navigation-item-active'"
[routerLinkActiveOptions]="isActiveMatchOptions">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
<ng-container *ngIf="item.link && !item.externalLink && !item.function && !item.disabled">
<div
class="fuse-horizontal-navigation-item"
[ngClass]="{'fuse-horizontal-navigation-item-active-forced': item.active}"
[routerLink]="[item.link]"
[routerLinkActive]="'fuse-horizontal-navigation-item-active'"
[routerLinkActiveOptions]="isActiveMatchOptions"
[matTooltip]="item.tooltip || ''">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
</ng-container>
<!-- Item with an external link -->
<a
class="fuse-horizontal-navigation-item"
*ngIf="item.link && item.externalLink && !item.function && !item.disabled"
[href]="item.link">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</a>
<ng-container *ngIf="item.link && item.externalLink && !item.function && !item.disabled">
<a
class="fuse-horizontal-navigation-item"
[href]="item.link"
[target]="item.target || '_self'"
[matTooltip]="item.tooltip || ''">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</a>
</ng-container>
<!-- Item with a function -->
<div
class="fuse-horizontal-navigation-item"
*ngIf="!item.link && item.function && !item.disabled"
[ngClass]="{'fuse-horizontal-navigation-item-active-forced': item.active}"
(click)="item.function(item)">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
<ng-container *ngIf="!item.link && item.function && !item.disabled">
<div
class="fuse-horizontal-navigation-item"
[ngClass]="{'fuse-horizontal-navigation-item-active-forced': item.active}"
[matTooltip]="item.tooltip || ''"
(click)="item.function(item)">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
</ng-container>
<!-- Item with an internal link and function -->
<div
class="fuse-horizontal-navigation-item"
*ngIf="item.link && !item.externalLink && item.function && !item.disabled"
[ngClass]="{'fuse-horizontal-navigation-item-active-forced': item.active}"
[routerLink]="[item.link]"
[routerLinkActive]="'fuse-horizontal-navigation-item-active'"
[routerLinkActiveOptions]="isActiveMatchOptions"
(click)="item.function(item)">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
<ng-container *ngIf="item.link && !item.externalLink && item.function && !item.disabled">
<div
class="fuse-horizontal-navigation-item"
[ngClass]="{'fuse-horizontal-navigation-item-active-forced': item.active}"
[routerLink]="[item.link]"
[routerLinkActive]="'fuse-horizontal-navigation-item-active'"
[routerLinkActiveOptions]="isActiveMatchOptions"
[matTooltip]="item.tooltip || ''"
(click)="item.function(item)">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
</ng-container>
<!-- Item with an external link and function -->
<a
class="fuse-horizontal-navigation-item"
*ngIf="item.link && item.externalLink && item.function && !item.disabled"
[href]="item.link"
(click)="item.function(item)"
mat-menu-item>
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</a>
<ng-container *ngIf="item.link && item.externalLink && item.function && !item.disabled">
<a
class="fuse-horizontal-navigation-item"
[href]="item.link"
[target]="item.target || '_self'"
[matTooltip]="item.tooltip || ''"
(click)="item.function(item)"
mat-menu-item>
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</a>
</ng-container>
<!-- Item with a no link and no function -->
<div
class="fuse-horizontal-navigation-item"
*ngIf="!item.link && !item.function && !item.disabled"
[ngClass]="{'fuse-horizontal-navigation-item-active-forced': item.active}">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
<ng-container *ngIf="!item.link && !item.function && !item.disabled">
<div
class="fuse-horizontal-navigation-item"
[ngClass]="{'fuse-horizontal-navigation-item-active-forced': item.active}"
[matTooltip]="item.tooltip || ''">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
</ng-container>
<!-- Item is disabled -->
<div
class="fuse-horizontal-navigation-item fuse-horizontal-navigation-item-disabled"
*ngIf="item.disabled">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
<ng-container *ngIf="item.disabled">
<div class="fuse-horizontal-navigation-item fuse-horizontal-navigation-item-disabled">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
</ng-container>
</div>
@@ -75,11 +89,12 @@
<ng-template #itemTemplate>
<!-- Icon -->
<mat-icon
class="fuse-horizontal-navigation-item-icon"
[ngClass]="item.classes?.icon"
*ngIf="item.icon"
[svgIcon]="item.icon"></mat-icon>
<ng-container *ngIf="item.icon">
<mat-icon
class="fuse-horizontal-navigation-item-icon"
[ngClass]="item.classes?.icon"
[svgIcon]="item.icon"></mat-icon>
</ng-container>
<!-- Title & Subtitle -->
<div class="fuse-horizontal-navigation-item-title-wrapper">
@@ -88,24 +103,24 @@
{{item.title}}
</span>
</div>
<div
class="fuse-horizontal-navigation-item-subtitle text-hint"
*ngIf="item.subtitle">
<span [ngClass]="item.classes?.subtitle">
{{item.subtitle}}
</span>
</div>
<ng-container *ngIf="item.subtitle">
<div class="fuse-horizontal-navigation-item-subtitle text-hint">
<span [ngClass]="item.classes?.subtitle">
{{item.subtitle}}
</span>
</div>
</ng-container>
</div>
<!-- Badge -->
<div
class="fuse-horizontal-navigation-item-badge"
*ngIf="item.badge">
<div
class="fuse-horizontal-navigation-item-badge-content"
[ngClass]="item.badge.classes">
{{item.badge.title}}
<ng-container *ngIf="item.badge">
<div class="fuse-horizontal-navigation-item-badge">
<div
class="fuse-horizontal-navigation-item-badge-content"
[ngClass]="item.badge.classes">
{{item.badge.title}}
</div>
</div>
</div>
</ng-container>
</ng-template>

View File

@@ -1,59 +1,63 @@
<div
*ngIf="!child"
[ngClass]="{'fuse-horizontal-navigation-menu-active': trigger.menuOpen,
<ng-container *ngIf="!child">
<div
[ngClass]="{'fuse-horizontal-navigation-menu-active': trigger.menuOpen,
'fuse-horizontal-navigation-menu-active-forced': item.active}"
[matMenuTriggerFor]="matMenu"
(onMenuOpen)="triggerChangeDetection()"
(onMenuClose)="triggerChangeDetection()"
#trigger="matMenuTrigger">
<ng-container *ngTemplateOutlet="itemTemplate; context: {$implicit: item}"></ng-container>
</div>
[matMenuTriggerFor]="matMenu"
(onMenuOpen)="triggerChangeDetection()"
(onMenuClose)="triggerChangeDetection()"
#trigger="matMenuTrigger">
<ng-container *ngTemplateOutlet="itemTemplate; context: {$implicit: item}"></ng-container>
</div>
</ng-container>
<mat-menu
class="fuse-horizontal-navigation-menu-panel"
[overlapTrigger]="false"
#matMenu="matMenu">
<ng-container *ngFor="let item of item.children">
<ng-container *ngFor="let item of item.children; trackBy: trackByFn">
<!-- Skip the hidden items -->
<ng-container *ngIf="(item.hidden && !item.hidden(item)) || !item.hidden">
<!-- Basic -->
<div
class="fuse-horizontal-navigation-menu-item"
*ngIf="item.type === 'basic'"
[disabled]="item.disabled"
mat-menu-item>
<fuse-horizontal-navigation-basic-item
[item]="item"
[name]="name"></fuse-horizontal-navigation-basic-item>
</div>
<ng-container *ngIf="item.type === 'basic'">
<div
class="fuse-horizontal-navigation-menu-item"
[disabled]="item.disabled"
mat-menu-item>
<fuse-horizontal-navigation-basic-item
[item]="item"
[name]="name"></fuse-horizontal-navigation-basic-item>
</div>
</ng-container>
<!-- Branch: aside, collapsable, group -->
<div
class="fuse-horizontal-navigation-menu-item"
*ngIf="item.type === 'aside' || item.type === 'collapsable' || item.type === 'group'"
[disabled]="item.disabled"
[matMenuTriggerFor]="branch.matMenu"
mat-menu-item>
<ng-container *ngTemplateOutlet="itemTemplate; context: {$implicit: item}"></ng-container>
<fuse-horizontal-navigation-branch-item
[child]="true"
[item]="item"
[name]="name"
#branch></fuse-horizontal-navigation-branch-item>
</div>
<ng-container *ngIf="item.type === 'aside' || item.type === 'collapsable' || item.type === 'group'">
<div
class="fuse-horizontal-navigation-menu-item"
[disabled]="item.disabled"
[matMenuTriggerFor]="branch.matMenu"
mat-menu-item>
<ng-container *ngTemplateOutlet="itemTemplate; context: {$implicit: item}"></ng-container>
<fuse-horizontal-navigation-branch-item
[child]="true"
[item]="item"
[name]="name"
#branch></fuse-horizontal-navigation-branch-item>
</div>
</ng-container>
<!-- Divider -->
<div
class="fuse-horizontal-navigation-menu-item"
*ngIf="item.type === 'divider'"
mat-menu-item>
<fuse-horizontal-navigation-divider-item
[item]="item"
[name]="name"></fuse-horizontal-navigation-divider-item>
</div>
<ng-container *ngIf="item.type === 'divider'">
<div
class="fuse-horizontal-navigation-menu-item"
mat-menu-item>
<fuse-horizontal-navigation-divider-item
[item]="item"
[name]="name"></fuse-horizontal-navigation-divider-item>
</div>
</ng-container>
</ng-container>
@@ -74,14 +78,16 @@
<div
class="fuse-horizontal-navigation-item"
[ngClass]="{'fuse-horizontal-navigation-item-disabled': item.disabled,
'fuse-horizontal-navigation-item-active-forced': item.active}">
'fuse-horizontal-navigation-item-active-forced': item.active}"
[matTooltip]="item.tooltip || ''">
<!-- Icon -->
<mat-icon
class="fuse-horizontal-navigation-item-icon"
[ngClass]="item.classes?.icon"
*ngIf="item.icon"
[svgIcon]="item.icon"></mat-icon>
<ng-container *ngIf="item.icon">
<mat-icon
class="fuse-horizontal-navigation-item-icon"
[ngClass]="item.classes?.icon"
[svgIcon]="item.icon"></mat-icon>
</ng-container>
<!-- Title & Subtitle -->
<div class="fuse-horizontal-navigation-item-title-wrapper">
@@ -90,25 +96,25 @@
{{item.title}}
</span>
</div>
<div
class="fuse-horizontal-navigation-item-subtitle text-hint"
*ngIf="item.subtitle">
<span [ngClass]="item.classes?.subtitle">
{{item.subtitle}}
</span>
</div>
<ng-container *ngIf="item.subtitle">
<div class="fuse-horizontal-navigation-item-subtitle text-hint">
<span [ngClass]="item.classes?.subtitle">
{{item.subtitle}}
</span>
</div>
</ng-container>
</div>
<!-- Badge -->
<div
class="fuse-horizontal-navigation-item-badge"
*ngIf="item.badge">
<div
class="fuse-horizontal-navigation-item-badge-content"
[ngClass]="item.badge.classes">
{{item.badge.title}}
<ng-container *ngIf="item.badge">
<div class="fuse-horizontal-navigation-item-badge">
<div
class="fuse-horizontal-navigation-item-badge-content"
[ngClass]="item.badge.classes">
{{item.badge.title}}
</div>
</div>
</div>
</ng-container>
</div>
</div>

View File

@@ -81,4 +81,15 @@ export class FuseHorizontalNavigationBranchItemComponent implements OnInit, OnDe
// Mark for check
this._changeDetectorRef.markForCheck();
}
/**
* Track by function for ngFor loops
*
* @param index
* @param item
*/
trackByFn(index: number, item: any): any
{
return item.id || index;
}
}

View File

@@ -1,30 +1,33 @@
<div class="fuse-horizontal-navigation-wrapper">
<ng-container *ngFor="let item of navigation">
<ng-container *ngFor="let item of navigation; trackBy: trackByFn">
<!-- Skip the hidden items -->
<ng-container *ngIf="(item.hidden && !item.hidden(item)) || !item.hidden">
<!-- Basic -->
<fuse-horizontal-navigation-basic-item
class="fuse-horizontal-navigation-menu-item"
*ngIf="item.type === 'basic'"
[item]="item"
[name]="name"></fuse-horizontal-navigation-basic-item>
<ng-container *ngIf="item.type === 'basic'">
<fuse-horizontal-navigation-basic-item
class="fuse-horizontal-navigation-menu-item"
[item]="item"
[name]="name"></fuse-horizontal-navigation-basic-item>
</ng-container>
<!-- Branch: aside, collapsable, group -->
<fuse-horizontal-navigation-branch-item
class="fuse-horizontal-navigation-menu-item"
*ngIf="item.type === 'aside' || item.type === 'collapsable' || item.type === 'group'"
[item]="item"
[name]="name"></fuse-horizontal-navigation-branch-item>
<ng-container *ngIf="item.type === 'aside' || item.type === 'collapsable' || item.type === 'group'">
<fuse-horizontal-navigation-branch-item
class="fuse-horizontal-navigation-menu-item"
[item]="item"
[name]="name"></fuse-horizontal-navigation-branch-item>
</ng-container>
<!-- Spacer -->
<fuse-horizontal-navigation-spacer-item
class="fuse-horizontal-navigation-menu-item"
*ngIf="item.type === 'spacer'"
[item]="item"
[name]="name"></fuse-horizontal-navigation-spacer-item>
<ng-container *ngIf="item.type === 'spacer'">
<fuse-horizontal-navigation-spacer-item
class="fuse-horizontal-navigation-menu-item"
[item]="item"
[name]="name"></fuse-horizontal-navigation-spacer-item>
</ng-container>
</ng-container>

View File

@@ -15,8 +15,15 @@ export interface FuseNavigationItem
hidden?: (item: FuseNavigationItem) => boolean;
active?: boolean;
disabled?: boolean;
tooltip?: string;
link?: string;
externalLink?: boolean;
target?:
| '_blank'
| '_self'
| '_parent'
| '_top'
| string;
exactMatch?: boolean;
isActiveMatchOptions?: IsActiveMatchOptions;
function?: (item: FuseNavigationItem) => void;

View File

@@ -7,14 +7,16 @@
class="fuse-vertical-navigation-item"
[ngClass]="{'fuse-vertical-navigation-item-active': active,
'fuse-vertical-navigation-item-disabled': item.disabled,
'fuse-vertical-navigation-item-active-forced': item.active}">
'fuse-vertical-navigation-item-active-forced': item.active}"
[matTooltip]="item.tooltip || ''">
<!-- Icon -->
<mat-icon
class="fuse-vertical-navigation-item-icon"
[ngClass]="item.classes?.icon"
*ngIf="item.icon"
[svgIcon]="item.icon"></mat-icon>
<ng-container *ngIf="item.icon">
<mat-icon
class="fuse-vertical-navigation-item-icon"
[ngClass]="item.classes?.icon"
[svgIcon]="item.icon"></mat-icon>
</ng-container>
<!-- Title & Subtitle -->
<div class="fuse-vertical-navigation-item-title-wrapper">
@@ -23,25 +25,25 @@
{{item.title}}
</span>
</div>
<div
class="fuse-vertical-navigation-item-subtitle"
*ngIf="item.subtitle">
<span [ngClass]="item.classes?.subtitle">
{{item.subtitle}}
</span>
</div>
<ng-container *ngIf="item.subtitle">
<div class="fuse-vertical-navigation-item-subtitle">
<span [ngClass]="item.classes?.subtitle">
{{item.subtitle}}
</span>
</div>
</ng-container>
</div>
<!-- Badge -->
<div
class="fuse-vertical-navigation-item-badge"
*ngIf="item.badge">
<div
class="fuse-vertical-navigation-item-badge-content"
[ngClass]="item.badge.classes">
{{item.badge.title}}
<ng-container *ngIf="item.badge">
<div class="fuse-vertical-navigation-item-badge">
<div
class="fuse-vertical-navigation-item-badge-content"
[ngClass]="item.badge.classes">
{{item.badge.title}}
</div>
</div>
</div>
</ng-container>
</div>
@@ -57,35 +59,40 @@
<ng-container *ngIf="(item.hidden && !item.hidden(item)) || !item.hidden">
<!-- Basic -->
<fuse-vertical-navigation-basic-item
*ngIf="item.type === 'basic'"
[item]="item"
[name]="name"></fuse-vertical-navigation-basic-item>
<ng-container *ngIf="item.type === 'basic'">
<fuse-vertical-navigation-basic-item
[item]="item"
[name]="name"></fuse-vertical-navigation-basic-item>
</ng-container>
<!-- Collapsable -->
<fuse-vertical-navigation-collapsable-item
*ngIf="item.type === 'collapsable'"
[item]="item"
[name]="name"
[autoCollapse]="autoCollapse"></fuse-vertical-navigation-collapsable-item>
<ng-container *ngIf="item.type === 'collapsable'">
<fuse-vertical-navigation-collapsable-item
[item]="item"
[name]="name"
[autoCollapse]="autoCollapse"></fuse-vertical-navigation-collapsable-item>
</ng-container>
<!-- Divider -->
<fuse-vertical-navigation-divider-item
*ngIf="item.type === 'divider'"
[item]="item"
[name]="name"></fuse-vertical-navigation-divider-item>
<ng-container *ngIf="item.type === 'divider'">
<fuse-vertical-navigation-divider-item
[item]="item"
[name]="name"></fuse-vertical-navigation-divider-item>
</ng-container>
<!-- Group -->
<fuse-vertical-navigation-group-item
*ngIf="item.type === 'group'"
[item]="item"
[name]="name"></fuse-vertical-navigation-group-item>
<ng-container *ngIf="item.type === 'group'">
<fuse-vertical-navigation-group-item
[item]="item"
[name]="name"></fuse-vertical-navigation-group-item>
</ng-container>
<!-- Spacer -->
<fuse-vertical-navigation-spacer-item
*ngIf="item.type === 'spacer'"
[item]="item"
[name]="name"></fuse-vertical-navigation-spacer-item>
<ng-container *ngIf="item.type === 'spacer'">
<fuse-vertical-navigation-spacer-item
[item]="item"
[name]="name"></fuse-vertical-navigation-spacer-item>
</ng-container>
</ng-container>

View File

@@ -5,68 +5,84 @@
[ngClass]="item.classes?.wrapper">
<!-- Item with an internal link -->
<a
class="fuse-vertical-navigation-item"
*ngIf="item.link && !item.externalLink && !item.function && !item.disabled"
[ngClass]="{'fuse-vertical-navigation-item-active-forced': item.active}"
[routerLink]="[item.link]"
[routerLinkActive]="'fuse-vertical-navigation-item-active'"
[routerLinkActiveOptions]="isActiveMatchOptions">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</a>
<ng-container *ngIf="item.link && !item.externalLink && !item.function && !item.disabled">
<a
class="fuse-vertical-navigation-item"
[ngClass]="{'fuse-vertical-navigation-item-active-forced': item.active}"
[routerLink]="[item.link]"
[routerLinkActive]="'fuse-vertical-navigation-item-active'"
[routerLinkActiveOptions]="isActiveMatchOptions"
[matTooltip]="item.tooltip || ''">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</a>
</ng-container>
<!-- Item with an external link -->
<a
class="fuse-vertical-navigation-item"
*ngIf="item.link && item.externalLink && !item.function && !item.disabled"
[href]="item.link">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</a>
<ng-container *ngIf="item.link && item.externalLink && !item.function && !item.disabled">
<a
class="fuse-vertical-navigation-item"
[href]="item.link"
[target]="item.target || '_self'"
[matTooltip]="item.tooltip || ''">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</a>
</ng-container>
<!-- Item with a function -->
<div
class="fuse-vertical-navigation-item"
*ngIf="!item.link && item.function && !item.disabled"
[ngClass]="{'fuse-vertical-navigation-item-active-forced': item.active}"
(click)="item.function(item)">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
<ng-container *ngIf="!item.link && item.function && !item.disabled">
<div
class="fuse-vertical-navigation-item"
[ngClass]="{'fuse-vertical-navigation-item-active-forced': item.active}"
[matTooltip]="item.tooltip || ''"
(click)="item.function(item)">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
</ng-container>
<!-- Item with an internal link and function -->
<a
class="fuse-vertical-navigation-item"
*ngIf="item.link && !item.externalLink && item.function && !item.disabled"
[ngClass]="{'fuse-vertical-navigation-item-active-forced': item.active}"
[routerLink]="[item.link]"
[routerLinkActive]="'fuse-vertical-navigation-item-active'"
[routerLinkActiveOptions]="isActiveMatchOptions"
(click)="item.function(item)">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</a>
<ng-container *ngIf="item.link && !item.externalLink && item.function && !item.disabled">
<a
class="fuse-vertical-navigation-item"
[ngClass]="{'fuse-vertical-navigation-item-active-forced': item.active}"
[routerLink]="[item.link]"
[routerLinkActive]="'fuse-vertical-navigation-item-active'"
[routerLinkActiveOptions]="isActiveMatchOptions"
[matTooltip]="item.tooltip || ''"
(click)="item.function(item)">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</a>
</ng-container>
<!-- Item with an external link and function -->
<a
class="fuse-vertical-navigation-item"
*ngIf="item.link && item.externalLink && item.function && !item.disabled"
[href]="item.link"
(click)="item.function(item)">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</a>
<ng-container *ngIf="item.link && item.externalLink && item.function && !item.disabled">
<a
class="fuse-vertical-navigation-item"
[href]="item.link"
[target]="item.target || '_self'"
[matTooltip]="item.tooltip || ''"
(click)="item.function(item)">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</a>
</ng-container>
<!-- Item with a no link and no function -->
<div
class="fuse-vertical-navigation-item"
*ngIf="!item.link && !item.function && !item.disabled"
[ngClass]="{'fuse-vertical-navigation-item-active-forced': item.active}">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
<ng-container *ngIf="!item.link && !item.function && !item.disabled">
<div
class="fuse-vertical-navigation-item"
[ngClass]="{'fuse-vertical-navigation-item-active-forced': item.active}"
[matTooltip]="item.tooltip || ''">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
</ng-container>
<!-- Item is disabled -->
<div
class="fuse-vertical-navigation-item fuse-vertical-navigation-item-disabled"
*ngIf="item.disabled">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
<ng-container *ngIf="item.disabled">
<div
class="fuse-vertical-navigation-item fuse-vertical-navigation-item-disabled"
[matTooltip]="item.tooltip || ''">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
</ng-container>
</div>
@@ -74,11 +90,12 @@
<ng-template #itemTemplate>
<!-- Icon -->
<mat-icon
class="fuse-vertical-navigation-item-icon"
[ngClass]="item.classes?.icon"
*ngIf="item.icon"
[svgIcon]="item.icon"></mat-icon>
<ng-container *ngIf="item.icon">
<mat-icon
class="fuse-vertical-navigation-item-icon"
[ngClass]="item.classes?.icon"
[svgIcon]="item.icon"></mat-icon>
</ng-container>
<!-- Title & Subtitle -->
<div class="fuse-vertical-navigation-item-title-wrapper">
@@ -87,24 +104,24 @@
{{item.title}}
</span>
</div>
<div
class="fuse-vertical-navigation-item-subtitle"
*ngIf="item.subtitle">
<span [ngClass]="item.classes?.subtitle">
{{item.subtitle}}
</span>
</div>
<ng-container *ngIf="item.subtitle">
<div class="fuse-vertical-navigation-item-subtitle">
<span [ngClass]="item.classes?.subtitle">
{{item.subtitle}}
</span>
</div>
</ng-container>
</div>
<!-- Badge -->
<div
class="fuse-vertical-navigation-item-badge"
*ngIf="item.badge">
<div
class="fuse-vertical-navigation-item-badge-content"
[ngClass]="item.badge.classes">
{{item.badge.title}}
<ng-container *ngIf="item.badge">
<div class="fuse-vertical-navigation-item-badge">
<div
class="fuse-vertical-navigation-item-badge-content"
[ngClass]="item.badge.classes">
{{item.badge.title}}
</div>
</div>
</div>
</ng-container>
</ng-template>

View File

@@ -6,14 +6,16 @@
<div
class="fuse-vertical-navigation-item"
[ngClass]="{'fuse-vertical-navigation-item-disabled': item.disabled}"
[matTooltip]="item.tooltip || ''"
(click)="toggleCollapsable()">
<!-- Icon -->
<mat-icon
class="fuse-vertical-navigation-item-icon"
[ngClass]="item.classes?.icon"
*ngIf="item.icon"
[svgIcon]="item.icon"></mat-icon>
<ng-container *ngIf="item.icon">
<mat-icon
class="fuse-vertical-navigation-item-icon"
[ngClass]="item.classes?.icon"
[svgIcon]="item.icon"></mat-icon>
</ng-container>
<!-- Title & Subtitle -->
<div class="fuse-vertical-navigation-item-title-wrapper">
@@ -22,25 +24,25 @@
{{item.title}}
</span>
</div>
<div
class="fuse-vertical-navigation-item-subtitle"
*ngIf="item.subtitle">
<span [ngClass]="item.classes?.subtitle">
{{item.subtitle}}
</span>
</div>
<ng-container *ngIf="item.subtitle">
<div class="fuse-vertical-navigation-item-subtitle">
<span [ngClass]="item.classes?.subtitle">
{{item.subtitle}}
</span>
</div>
</ng-container>
</div>
<!-- Badge -->
<div
class="fuse-vertical-navigation-item-badge"
*ngIf="item.badge">
<div
class="fuse-vertical-navigation-item-badge-content"
[ngClass]="item.badge.classes">
{{item.badge.title}}
<ng-container *ngIf="item.badge">
<div class="fuse-vertical-navigation-item-badge">
<div
class="fuse-vertical-navigation-item-badge-content"
[ngClass]="item.badge.classes">
{{item.badge.title}}
</div>
</div>
</div>
</ng-container>
<!-- Arrow -->
<mat-icon
@@ -62,35 +64,40 @@
<ng-container *ngIf="(item.hidden && !item.hidden(item)) || !item.hidden">
<!-- Basic -->
<fuse-vertical-navigation-basic-item
*ngIf="item.type === 'basic'"
[item]="item"
[name]="name"></fuse-vertical-navigation-basic-item>
<ng-container *ngIf="item.type === 'basic'">
<fuse-vertical-navigation-basic-item
[item]="item"
[name]="name"></fuse-vertical-navigation-basic-item>
</ng-container>
<!-- Collapsable -->
<fuse-vertical-navigation-collapsable-item
*ngIf="item.type === 'collapsable'"
[item]="item"
[name]="name"
[autoCollapse]="autoCollapse"></fuse-vertical-navigation-collapsable-item>
<ng-container *ngIf="item.type === 'collapsable'">
<fuse-vertical-navigation-collapsable-item
[item]="item"
[name]="name"
[autoCollapse]="autoCollapse"></fuse-vertical-navigation-collapsable-item>
</ng-container>
<!-- Divider -->
<fuse-vertical-navigation-divider-item
*ngIf="item.type === 'divider'"
[item]="item"
[name]="name"></fuse-vertical-navigation-divider-item>
<ng-container *ngIf="item.type === 'divider'">
<fuse-vertical-navigation-divider-item
[item]="item"
[name]="name"></fuse-vertical-navigation-divider-item>
</ng-container>
<!-- Group -->
<fuse-vertical-navigation-group-item
*ngIf="item.type === 'group'"
[item]="item"
[name]="name"></fuse-vertical-navigation-group-item>
<ng-container *ngIf="item.type === 'group'">
<fuse-vertical-navigation-group-item
[item]="item"
[name]="name"></fuse-vertical-navigation-group-item>
</ng-container>
<!-- Spacer -->
<fuse-vertical-navigation-spacer-item
*ngIf="item.type === 'spacer'"
[item]="item"
[name]="name"></fuse-vertical-navigation-spacer-item>
<ng-container *ngIf="item.type === 'spacer'">
<fuse-vertical-navigation-spacer-item
[item]="item"
[name]="name"></fuse-vertical-navigation-spacer-item>
</ng-container>
</ng-container>

View File

@@ -7,11 +7,12 @@
<div class="fuse-vertical-navigation-item">
<!-- Icon -->
<mat-icon
class="fuse-vertical-navigation-item-icon"
[ngClass]="item.classes?.icon"
*ngIf="item.icon"
[svgIcon]="item.icon"></mat-icon>
<ng-container *ngIf="item.icon">
<mat-icon
class="fuse-vertical-navigation-item-icon"
[ngClass]="item.classes?.icon"
[svgIcon]="item.icon"></mat-icon>
</ng-container>
<!-- Title & Subtitle -->
<div class="fuse-vertical-navigation-item-title-wrapper">
@@ -20,25 +21,25 @@
{{item.title}}
</span>
</div>
<div
class="fuse-vertical-navigation-item-subtitle"
*ngIf="item.subtitle">
<span [ngClass]="item.classes?.subtitle">
{{item.subtitle}}
</span>
</div>
<ng-container *ngIf="item.subtitle">
<div class="fuse-vertical-navigation-item-subtitle">
<span [ngClass]="item.classes?.subtitle">
{{item.subtitle}}
</span>
</div>
</ng-container>
</div>
<!-- Badge -->
<div
class="fuse-vertical-navigation-item-badge"
*ngIf="item.badge">
<div
class="fuse-vertical-navigation-item-badge-content"
[ngClass]="item.badge.classes">
{{item.badge.title}}
<ng-container *ngIf="item.badge">
<div class="fuse-vertical-navigation-item-badge">
<div
class="fuse-vertical-navigation-item-badge-content"
[ngClass]="item.badge.classes">
{{item.badge.title}}
</div>
</div>
</div>
</ng-container>
</div>
@@ -50,35 +51,40 @@
<ng-container *ngIf="(item.hidden && !item.hidden(item)) || !item.hidden">
<!-- Basic -->
<fuse-vertical-navigation-basic-item
*ngIf="item.type === 'basic'"
[item]="item"
[name]="name"></fuse-vertical-navigation-basic-item>
<ng-container *ngIf="item.type === 'basic'">
<fuse-vertical-navigation-basic-item
[item]="item"
[name]="name"></fuse-vertical-navigation-basic-item>
</ng-container>
<!-- Collapsable -->
<fuse-vertical-navigation-collapsable-item
*ngIf="item.type === 'collapsable'"
[item]="item"
[name]="name"
[autoCollapse]="autoCollapse"></fuse-vertical-navigation-collapsable-item>
<ng-container *ngIf="item.type === 'collapsable'">
<fuse-vertical-navigation-collapsable-item
[item]="item"
[name]="name"
[autoCollapse]="autoCollapse"></fuse-vertical-navigation-collapsable-item>
</ng-container>
<!-- Divider -->
<fuse-vertical-navigation-divider-item
*ngIf="item.type === 'divider'"
[item]="item"
[name]="name"></fuse-vertical-navigation-divider-item>
<ng-container *ngIf="item.type === 'divider'">
<fuse-vertical-navigation-divider-item
[item]="item"
[name]="name"></fuse-vertical-navigation-divider-item>
</ng-container>
<!-- Group -->
<fuse-vertical-navigation-group-item
*ngIf="item.type === 'group'"
[item]="item"
[name]="name"></fuse-vertical-navigation-group-item>
<ng-container *ngIf="item.type === 'group'">
<fuse-vertical-navigation-group-item
[item]="item"
[name]="name"></fuse-vertical-navigation-group-item>
</ng-container>
<!-- Spacer -->
<fuse-vertical-navigation-spacer-item
*ngIf="item.type === 'spacer'"
[item]="item"
[name]="name"></fuse-vertical-navigation-spacer-item>
<ng-container *ngIf="item.type === 'spacer'">
<fuse-vertical-navigation-spacer-item
[item]="item"
[name]="name"></fuse-vertical-navigation-spacer-item>
</ng-container>
</ng-container>

View File

@@ -335,6 +335,7 @@ fuse-vertical-navigation {
}
> .fuse-vertical-navigation-item-children {
margin-top: 6px;
> *:last-child {
padding-bottom: 6px;

View File

@@ -24,46 +24,52 @@
<ng-container *ngIf="(item.hidden && !item.hidden(item)) || !item.hidden">
<!-- Aside -->
<fuse-vertical-navigation-aside-item
*ngIf="item.type === 'aside'"
[item]="item"
[name]="name"
[activeItemId]="activeAsideItemId"
[autoCollapse]="autoCollapse"
[skipChildren]="true"
(click)="toggleAside(item)"></fuse-vertical-navigation-aside-item>
<ng-container *ngIf="item.type === 'aside'">
<fuse-vertical-navigation-aside-item
[item]="item"
[name]="name"
[activeItemId]="activeAsideItemId"
[autoCollapse]="autoCollapse"
[skipChildren]="true"
(click)="toggleAside(item)"></fuse-vertical-navigation-aside-item>
</ng-container>
<!-- Basic -->
<fuse-vertical-navigation-basic-item
*ngIf="item.type === 'basic'"
[item]="item"
[name]="name"></fuse-vertical-navigation-basic-item>
<ng-container *ngIf="item.type === 'basic'">
<fuse-vertical-navigation-basic-item
[item]="item"
[name]="name"></fuse-vertical-navigation-basic-item>
</ng-container>
<!-- Collapsable -->
<fuse-vertical-navigation-collapsable-item
*ngIf="item.type === 'collapsable'"
[item]="item"
[name]="name"
[autoCollapse]="autoCollapse"></fuse-vertical-navigation-collapsable-item>
<ng-container *ngIf="item.type === 'collapsable'">
<fuse-vertical-navigation-collapsable-item
[item]="item"
[name]="name"
[autoCollapse]="autoCollapse"></fuse-vertical-navigation-collapsable-item>
</ng-container>
<!-- Divider -->
<fuse-vertical-navigation-divider-item
*ngIf="item.type === 'divider'"
[item]="item"
[name]="name"></fuse-vertical-navigation-divider-item>
<ng-container *ngIf="item.type === 'divider'">
<fuse-vertical-navigation-divider-item
[item]="item"
[name]="name"></fuse-vertical-navigation-divider-item>
</ng-container>
<!-- Group -->
<fuse-vertical-navigation-group-item
*ngIf="item.type === 'group'"
[item]="item"
[name]="name"
[autoCollapse]="autoCollapse"></fuse-vertical-navigation-group-item>
<ng-container *ngIf="item.type === 'group'">
<fuse-vertical-navigation-group-item
[item]="item"
[name]="name"
[autoCollapse]="autoCollapse"></fuse-vertical-navigation-group-item>
</ng-container>
<!-- Spacer -->
<fuse-vertical-navigation-spacer-item
*ngIf="item.type === 'spacer'"
[item]="item"
[name]="name"></fuse-vertical-navigation-spacer-item>
<ng-container *ngIf="item.type === 'spacer'">
<fuse-vertical-navigation-spacer-item
[item]="item"
[name]="name"></fuse-vertical-navigation-spacer-item>
</ng-container>
</ng-container>
@@ -84,31 +90,33 @@
</div>
<!-- Aside -->
<div
class="fuse-vertical-navigation-aside-wrapper"
*ngIf="activeAsideItemId"
fuseScrollbar
[fuseScrollbarOptions]="{wheelPropagation: false, suppressScrollX: true}"
[@fadeInLeft]="position === 'left'"
[@fadeInRight]="position === 'right'"
[@fadeOutLeft]="position === 'left'"
[@fadeOutRight]="position === 'right'">
<ng-container *ngIf="activeAsideItemId">
<div
class="fuse-vertical-navigation-aside-wrapper"
fuseScrollbar
[fuseScrollbarOptions]="{wheelPropagation: false, suppressScrollX: true}"
[@fadeInLeft]="position === 'left'"
[@fadeInRight]="position === 'right'"
[@fadeOutLeft]="position === 'left'"
[@fadeOutRight]="position === 'right'">
<!-- Items -->
<ng-container *ngFor="let item of navigation; trackBy: trackByFn">
<!-- Items -->
<ng-container *ngFor="let item of navigation; trackBy: trackByFn">
<!-- Skip the hidden items -->
<ng-container *ngIf="(item.hidden && !item.hidden(item)) || !item.hidden">
<!-- Skip the hidden items -->
<ng-container *ngIf="(item.hidden && !item.hidden(item)) || !item.hidden">
<!-- Aside -->
<fuse-vertical-navigation-aside-item
*ngIf="item.type === 'aside' && item.id === activeAsideItemId"
[item]="item"
[name]="name"
[autoCollapse]="autoCollapse"></fuse-vertical-navigation-aside-item>
<!-- Aside -->
<ng-container *ngIf="item.type === 'aside' && item.id === activeAsideItemId">
<fuse-vertical-navigation-aside-item
[item]="item"
[name]="name"
[autoCollapse]="autoCollapse"></fuse-vertical-navigation-aside-item>
</ng-container>
</ng-container>
</ng-container>
</ng-container>
</div>
</div>
</ng-container>

View File

@@ -374,6 +374,10 @@ export class FuseVerticalNavigationComponent implements OnChanges, OnInit, After
*/
ngOnDestroy(): void
{
// Forcefully close the navigation and aside in case they are opened
this.close();
this.closeAside();
// Deregister the navigation component from the registry
this._fuseNavigationService.deregisterComponent(this.name);

View File

@@ -1,114 +0,0 @@
import { ChangeDetectorRef, Directive, ElementRef, HostBinding, HostListener, Input, NgZone, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core';
import { Subject } from 'rxjs';
@Directive({
selector: 'textarea[fuseAutogrow]',
exportAs: 'fuseAutogrow'
})
export class FuseAutogrowDirective implements OnChanges, OnInit, OnDestroy
{
// eslint-disable-next-line @angular-eslint/no-input-rename
@Input('fuseAutogrowVerticalPadding') padding: number = 8;
@HostBinding('rows') private _rows: number = 1;
private _height: string = 'auto';
private _unsubscribeAll: Subject<any> = new Subject<any>();
/**
* Constructor
*/
constructor(
private _elementRef: ElementRef,
private _changeDetectorRef: ChangeDetectorRef,
private _ngZone: NgZone
)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Accessors
// -----------------------------------------------------------------------------------------------------
/**
* Host binding for component inline styles
*/
@HostBinding('style') get styleList(): any
{
return {
'height' : this._height,
'overflow': 'hidden',
'resize' : 'none'
};
}
// -----------------------------------------------------------------------------------------------------
// @ Decorated methods
// -----------------------------------------------------------------------------------------------------
/**
* Resize on 'input' and 'ngModelChange' events
*
* @private
*/
@HostListener('input')
@HostListener('ngModelChange')
private _resize(): void
{
// This doesn't need to trigger Angular's change detection by itself
this._ngZone.runOutsideAngular(() => {
setTimeout(() => {
// Set the height to 'auto' so we can correctly read the scrollHeight
this._height = 'auto';
// Detect the changes so the height is applied
this._changeDetectorRef.detectChanges();
// Get the scrollHeight and subtract the vertical padding
this._height = `${this._elementRef.nativeElement.scrollHeight - this.padding}px`;
// Detect the changes one more time to apply the final height
this._changeDetectorRef.detectChanges();
});
});
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On changes
*
* @param changes
*/
ngOnChanges(changes: SimpleChanges): void
{
// Padding
if ( 'fuseAutogrowVerticalPadding' in changes )
{
// Resize
this._resize();
}
}
/**
* On init
*/
ngOnInit(): void
{
// Resize for the first time
this._resize();
}
/**
* On destroy
*/
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
}

View File

@@ -1,14 +0,0 @@
import { NgModule } from '@angular/core';
import { FuseAutogrowDirective } from '@fuse/directives/autogrow/autogrow.directive';
@NgModule({
declarations: [
FuseAutogrowDirective
],
exports : [
FuseAutogrowDirective
]
})
export class FuseAutogrowModule
{
}

View File

@@ -1 +0,0 @@
export * from '@fuse/directives/autogrow/public-api';

View File

@@ -1,2 +0,0 @@
export * from '@fuse/directives/autogrow/autogrow.directive';
export * from '@fuse/directives/autogrow/autogrow.module';

View File

@@ -136,11 +136,8 @@
.mat-flat-button,
.mat-raised-button,
.mat-stroked-button {
.fuse-mat-rounded & {
padding: 0 20px;
border-radius: 9999px;
}
padding: 0 20px !important;
border-radius: 9999px !important;
}
/* Target all buttons */
@@ -334,6 +331,8 @@
/* @ Button Toggle
/* ----------------------------------------------------------------------------------------------------- */
.mat-button-toggle-group {
border: none !important;
@apply space-x-1;
&.mat-button-toggle-group-appearance-standard {
@@ -341,36 +340,27 @@
background-clip: padding-box;
}
}
}
/* Rounded design */
.fuse-mat-rounded {
.mat-button-toggle-group {
.mat-button-toggle {
border-radius: 9999px;
overflow: hidden;
border: none !important;
@apply space-x-1;
font-weight: 500;
.mat-button-toggle {
border-radius: 9999px;
overflow: hidden;
border: none !important;
font-weight: 500;
&.mat-button-toggle-checked {
.mat-button-toggle-label-content {
@apply text-default #{'!important'};
}
}
&.mat-button-toggle-checked {
.mat-button-toggle-label-content {
padding: 0 20px;
@apply text-secondary;
@apply text-default #{'!important'};
}
}
.mat-ripple {
border-radius: 9999px;
}
.mat-button-toggle-label-content {
padding: 0 20px;
@apply text-secondary;
}
.mat-ripple {
border-radius: 9999px;
}
}
}
@@ -410,6 +400,13 @@
font-weight: 500 !important;
}
/* ----------------------------------------------------------------------------------------------------- */
/* @ Dialog
/* ----------------------------------------------------------------------------------------------------- */
.mat-dialog-container {
border-radius: 16px !important;
}
/* ----------------------------------------------------------------------------------------------------- */
/* @ Drawer
/* ----------------------------------------------------------------------------------------------------- */
@@ -683,8 +680,8 @@
align-self: stretch;
min-height: 36px;
height: auto;
margin: 10px 0;
padding: 4px 6px 4px 0 !important;
margin: 14px 0;
padding: 0 6px 0 0;
transform: none;
}
@@ -1322,62 +1319,55 @@
opacity: 0 !important;
}
}
.mat-tab-header {
border-bottom: none !important;
.mat-tab-label-container {
padding: 0 24px;
.mat-tab-list {
.mat-tab-labels {
.mat-tab-label {
min-width: 0 !important;
height: 40px !important;
padding: 0 20px !important;
border-radius: 9999px !important;
@apply text-secondary;
&.mat-tab-label-active {
@apply bg-gray-700 bg-opacity-10 dark:bg-gray-50 dark:bg-opacity-10 #{'!important'};
@apply text-default #{'!important'};
}
+ .mat-tab-label {
margin-left: 4px;
}
.mat-tab-label-content {
line-height: 20px;
}
}
}
.mat-ink-bar {
display: none !important;
}
}
}
}
.mat-tab-body-content {
padding: 24px;
}
}
.mat-tab-label {
opacity: 1 !important;
}
/* Rounded design */
.fuse-mat-rounded {
.mat-tab-group {
.mat-tab-header {
border-bottom: none !important;
.mat-tab-label-container {
padding: 0 24px;
.mat-tab-list {
.mat-tab-labels {
.mat-tab-label {
min-width: 0 !important;
height: 40px !important;
padding: 0 20px !important;
border-radius: 9999px !important;
@apply text-secondary;
&.mat-tab-label-active {
@apply bg-gray-700 bg-opacity-10 dark:bg-gray-50 dark:bg-opacity-10 #{'!important'};
@apply text-default #{'!important'};
}
+ .mat-tab-label {
margin-left: 4px;
}
.mat-tab-label-content {
line-height: 20px;
}
}
}
.mat-ink-bar {
display: none !important;
}
}
}
}
.mat-tab-body-content {
padding: 24px;
}
}
}
/* ----------------------------------------------------------------------------------------------------- */
/* @ Textarea
/* ----------------------------------------------------------------------------------------------------- */

View File

@@ -1,21 +1,24 @@
@use '~@angular/material' as mat;
@use "sass:map";
/** Include the core Angular Material styles */
/* Include the core Angular Material styles */
@include mat.core();
/** Configure the Angular Material typography */
@include mat.all-component-typographies(
mat.define-typography-config(
$font-family: theme('fontFamily.sans'),
$title: mat.define-typography-level(1.25rem, 2rem, 600),
$body-2: mat.define-typography-level(0.875rem, 1.5rem, 600),
$button: mat.define-typography-level(0.875rem, 0.875rem, 500),
$input: mat.define-typography-level(0.875rem, 1.2857142857, 400) // line-height: 20px
)
);
/* Create a base theme without color.
This will globally set the density and typography for all future color themes. */
@include mat.all-component-themes((
color: null,
density: -2,
typography: mat.define-typography-config(
$font-family: theme('fontFamily.sans'),
$title: mat.define-typography-level(1.25rem, 2rem, 600),
$body-2: mat.define-typography-level(0.875rem, 1.5rem, 600),
$button: mat.define-typography-level(0.875rem, 0.875rem, 500),
$input: mat.define-typography-level(0.875rem, 1.2857142857, 400) /* line-height: 20px */
)
));
/** Prepare the Background and Foreground maps */
/* Prepare the Background and Foreground maps */
$background-light: (
status-bar: #CBD5E1, /* blueGray.300 */
app-bar: #FFFFFF,
@@ -90,7 +93,7 @@ $foreground-dark: (
slider-off-active: #94A3B8 /* blueGray.400 */
);
/** Generate Primary, Accent and Warn palettes */
/* Generate Primary, Accent and Warn palettes */
$palettes: ();
@each $name in (primary, accent, warn) {
$palettes: map.merge($palettes, (#{$name}: (
@@ -126,7 +129,7 @@ $palettes: ();
)));
}
/** Generate Angular Material themes. Since we are using CSS Custom Properties,
/* Generate Angular Material themes. Since we are using CSS Custom Properties,
we don't have to generate a separate Angular Material theme for each color
set. We can just create one light and one dark theme and then switch the
CSS Custom Properties to dynamically switch the colors. */
@@ -144,11 +147,11 @@ body .light {
is-dark: map.get(map.get($base-light-theme, color), is-dark),
foreground: $foreground-light,
background: $background-light
),
density: -2
)
);
@include mat.all-component-themes($light-theme);
/* Use all-component-colors to only generate the colors */
@include mat.all-component-colors($light-theme);
}
body.dark,
@@ -165,9 +168,9 @@ body .dark {
is-dark: map.get(map.get($base-dark-theme, color), is-dark),
foreground: $foreground-dark,
background: $background-dark
),
density: -2
)
);
/* Use all-component-colors to only generate the colors */
@include mat.all-component-colors($dark-theme);
}

View File

@@ -1,3 +1,3 @@
import { Version } from '@fuse/version/version';
export const FUSE_VERSION = new Version('13.0.2').full;
export const FUSE_VERSION = new Version('13.2.0').full;

View File

@@ -1,9 +1,11 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
import { forkJoin, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { InitialData } from 'app/app.types';
import { MessagesService } from 'app/layout/common/messages/messages.service';
import { NavigationService } from 'app/core/navigation/navigation.service';
import { NotificationsService } from 'app/layout/common/notifications/notifications.service';
import { ShortcutsService } from 'app/layout/common/shortcuts/shortcuts.service';
import { UserService } from 'app/core/user/user.service';
@Injectable({
providedIn: 'root'
@@ -13,7 +15,13 @@ export class InitialDataResolver implements Resolve<any>
/**
* Constructor
*/
constructor(private _httpClient: HttpClient)
constructor(
private _messagesService: MessagesService,
private _navigationService: NavigationService,
private _notificationsService: NotificationsService,
private _shortcutsService: ShortcutsService,
private _userService: UserService
)
{
}
@@ -27,29 +35,15 @@ export class InitialDataResolver implements Resolve<any>
* @param route
* @param state
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<InitialData>
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any>
{
// Fork join multiple API endpoint calls to wait all of them to finish
return forkJoin([
this._httpClient.get<any>('api/common/messages'),
this._httpClient.get<any>('api/common/navigation'),
this._httpClient.get<any>('api/common/notifications'),
this._httpClient.get<any>('api/common/shortcuts'),
this._httpClient.get<any>('api/common/user')
]).pipe(
map(([messages, navigation, notifications, shortcuts, user]) => ({
messages,
navigation: {
compact : navigation.compact,
default : navigation.default,
futuristic: navigation.futuristic,
horizontal: navigation.horizontal
},
notifications,
shortcuts,
user
})
)
);
this._navigationService.get(),
this._messagesService.getAll(),
this._notificationsService.getAll(),
this._shortcutsService.getAll(),
this._userService.get()
]);
}
}

View File

@@ -92,6 +92,7 @@ export const appRoutes: Route[] = [
{path: 'help-center', loadChildren: () => import('app/modules/admin/apps/help-center/help-center.module').then(m => m.HelpCenterModule)},
{path: 'mailbox', loadChildren: () => import('app/modules/admin/apps/mailbox/mailbox.module').then(m => m.MailboxModule)},
{path: 'notes', loadChildren: () => import('app/modules/admin/apps/notes/notes.module').then(m => m.NotesModule)},
{path: 'scrumboard', loadChildren: () => import('app/modules/admin/apps/scrumboard/scrumboard.module').then(m => m.ScrumboardModule)},
{path: 'tasks', loadChildren: () => import('app/modules/admin/apps/tasks/tasks.module').then(m => m.TasksModule)},
]},
@@ -148,6 +149,9 @@ export const appRoutes: Route[] = [
// TailwindCSS
{path: 'tailwindcss', loadChildren: () => import('app/modules/admin/ui/tailwindcss/tailwindcss.module').then(m => m.TailwindCSSModule)},
// Advanced search
{path: 'advanced-search', loadChildren: () => import('app/modules/admin/ui/advanced-search/advanced-search.module').then(m => m.AdvancedSearchModule)},
// Animations
{path: 'animations', loadChildren: () => import('app/modules/admin/ui/animations/animations.module').then(m => m.AnimationsModule)},

View File

@@ -1,19 +0,0 @@
import { FuseNavigationItem } from '@fuse/components/navigation';
import { Message } from 'app/layout/common/messages/messages.types';
import { Notification } from 'app/layout/common/notifications/notifications.types';
import { Shortcut } from 'app/layout/common/shortcuts/shortcuts.types';
import { User } from 'app/core/user/user.model';
export interface InitialData
{
messages: Message[];
navigation: {
compact: FuseNavigationItem[];
default: FuseNavigationItem[];
futuristic: FuseNavigationItem[];
horizontal: FuseNavigationItem[];
};
notifications: Notification[];
shortcuts: Shortcut[];
user: User;
}

View File

@@ -0,0 +1,48 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, ReplaySubject } from 'rxjs';
import { tap } from 'rxjs/operators';
import { Navigation } from 'app/core/navigation/navigation.types';
@Injectable({
providedIn: 'root'
})
export class NavigationService
{
private _navigation: ReplaySubject<Navigation> = new ReplaySubject<Navigation>(1);
/**
* Constructor
*/
constructor(private _httpClient: HttpClient)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Accessors
// -----------------------------------------------------------------------------------------------------
/**
* Getter for navigation
*/
get navigation$(): Observable<Navigation>
{
return this._navigation.asObservable();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Get all navigation data
*/
get(): Observable<Navigation>
{
return this._httpClient.get<Navigation>('api/common/navigation').pipe(
tap((navigation) => {
this._navigation.next(navigation);
})
);
}
}

View File

@@ -0,0 +1,9 @@
import { FuseNavigationItem } from '@fuse/components/navigation';
export interface Navigation
{
compact: FuseNavigationItem[];
default: FuseNavigationItem[];
futuristic: FuseNavigationItem[];
horizontal: FuseNavigationItem[];
}

View File

@@ -27,6 +27,6 @@ export class TranslocoHttpLoader implements TranslocoLoader
*/
getTranslation(lang: string): Observable<Translation>
{
return this._httpClient.get<Translation>(`/assets/i18n/${lang}.json`);
return this._httpClient.get<Translation>(`./assets/i18n/${lang}.json`);
}
}

View File

@@ -1,8 +1,8 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, ReplaySubject } from 'rxjs';
import { map } from 'rxjs/operators';
import { User } from 'app/core/user/user.model';
import { map, tap } from 'rxjs/operators';
import { User } from 'app/core/user/user.types';
@Injectable({
providedIn: 'root'
@@ -42,6 +42,18 @@ export class UserService
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Get the current logged in user data
*/
get(): Observable<User>
{
return this._httpClient.get<User>('api/common/user').pipe(
tap((user) => {
this._user.next(user);
})
);
}
/**
* Update the user
*
@@ -51,7 +63,6 @@ export class UserService
{
return this._httpClient.patch<User>('api/common/user', {user}).pipe(
map((response) => {
// Execute the observable
this._user.next(response);
})
);

View File

@@ -4,13 +4,13 @@ import { AvailableLangs, TranslocoService } from '@ngneat/transloco';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation';
@Component({
selector : 'language',
templateUrl : './language.component.html',
selector : 'languages',
templateUrl : './languages.component.html',
encapsulation : ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
exportAs : 'language'
exportAs : 'languages'
})
export class LanguageComponent implements OnInit, OnDestroy
export class LanguagesComponent implements OnInit, OnDestroy
{
availableLangs: AvailableLangs;
activeLang: string;

View File

@@ -2,12 +2,12 @@ import { NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { LanguageComponent } from 'app/layout/common/language/language.component';
import { LanguagesComponent } from 'app/layout/common/languages/languages.component';
import { SharedModule } from 'app/shared/shared.module';
@NgModule({
declarations: [
LanguageComponent
LanguagesComponent
],
imports : [
MatButtonModule,
@@ -16,9 +16,9 @@ import { SharedModule } from 'app/shared/shared.module';
SharedModule
],
exports : [
LanguageComponent
LanguagesComponent
]
})
export class LanguageModule
export class LanguagesModule
{
}

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, TemplateRef, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, TemplateRef, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core';
import { Overlay, OverlayRef } from '@angular/cdk/overlay';
import { TemplatePortal } from '@angular/cdk/portal';
import { MatButton } from '@angular/material/button';
@@ -14,12 +14,12 @@ import { MessagesService } from 'app/layout/common/messages/messages.service';
changeDetection: ChangeDetectionStrategy.OnPush,
exportAs : 'messages'
})
export class MessagesComponent implements OnInit, OnChanges, OnDestroy
export class MessagesComponent implements OnInit, OnDestroy
{
@Input() messages: Message[];
@ViewChild('messagesOrigin') private _messagesOrigin: MatButton;
@ViewChild('messagesPanel') private _messagesPanel: TemplateRef<any>;
messages: Message[];
unreadCount: number = 0;
private _overlayRef: OverlayRef;
private _unsubscribeAll: Subject<any> = new Subject<any>();
@@ -40,21 +40,6 @@ export class MessagesComponent implements OnInit, OnChanges, OnDestroy
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On changes
*
* @param changes
*/
ngOnChanges(changes: SimpleChanges): void
{
// Messages
if ( 'messages' in changes )
{
// Store the messages on the service
this._messagesService.store(changes.messages.currentValue);
}
}
/**
* On init
*/
@@ -182,7 +167,7 @@ export class MessagesComponent implements OnInit, OnChanges, OnDestroy
scrollStrategy : this._overlay.scrollStrategies.block(),
positionStrategy: this._overlay.position()
.flexibleConnectedTo(this._messagesOrigin._elementRef.nativeElement)
.withLockedPosition()
.withLockedPosition(true)
.withPush(true)
.withPositions([
{

View File

@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, ReplaySubject } from 'rxjs';
import { Message } from 'app/layout/common/messages/messages.types';
import { map, switchMap, take } from 'rxjs/operators';
import { map, switchMap, take, tap } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
@@ -35,17 +35,15 @@ export class MessagesService
// -----------------------------------------------------------------------------------------------------
/**
* Store messages on the service
*
* @param messages
* Get all messages
*/
store(messages: Message[]): Observable<Message[]>
getAll(): Observable<Message[]>
{
// Load the messages
this._messages.next(messages);
// Return the messages
return this.messages$;
return this._httpClient.get<Message[]>('api/common/messages').pipe(
tap((messages) => {
this._messages.next(messages);
})
);
}
/**

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, TemplateRef, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, TemplateRef, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core';
import { Overlay, OverlayRef } from '@angular/cdk/overlay';
import { TemplatePortal } from '@angular/cdk/portal';
import { MatButton } from '@angular/material/button';
@@ -14,12 +14,12 @@ import { NotificationsService } from 'app/layout/common/notifications/notificati
changeDetection: ChangeDetectionStrategy.OnPush,
exportAs : 'notifications'
})
export class NotificationsComponent implements OnChanges, OnInit, OnDestroy
export class NotificationsComponent implements OnInit, OnDestroy
{
@Input() notifications: Notification[];
@ViewChild('notificationsOrigin') private _notificationsOrigin: MatButton;
@ViewChild('notificationsPanel') private _notificationsPanel: TemplateRef<any>;
notifications: Notification[];
unreadCount: number = 0;
private _overlayRef: OverlayRef;
private _unsubscribeAll: Subject<any> = new Subject<any>();
@@ -40,21 +40,6 @@ export class NotificationsComponent implements OnChanges, OnInit, OnDestroy
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On changes
*
* @param changes
*/
ngOnChanges(changes: SimpleChanges): void
{
// Notifications
if ( 'notifications' in changes )
{
// Store the notifications on the service
this._notificationsService.store(changes.notifications.currentValue);
}
}
/**
* On init
*/
@@ -182,7 +167,7 @@ export class NotificationsComponent implements OnChanges, OnInit, OnDestroy
scrollStrategy : this._overlay.scrollStrategies.block(),
positionStrategy: this._overlay.position()
.flexibleConnectedTo(this._notificationsOrigin._elementRef.nativeElement)
.withLockedPosition()
.withLockedPosition(true)
.withPush(true)
.withPositions([
{

View File

@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, ReplaySubject } from 'rxjs';
import { Notification } from 'app/layout/common/notifications/notifications.types';
import { map, switchMap, take } from 'rxjs/operators';
import { map, switchMap, take, tap } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
@@ -35,17 +35,15 @@ export class NotificationsService
// -----------------------------------------------------------------------------------------------------
/**
* Store notifications on the service
*
* @param notifications
* Get all notifications
*/
store(notifications: Notification[]): Observable<Notification[]>
getAll(): Observable<Notification[]>
{
// Load the notifications
this._notifications.next(notifications);
// Return the notifications
return this.notifications$;
return this._httpClient.get<Notification[]>('api/common/notifications').pipe(
tap((notifications) => {
this._notifications.next(notifications);
})
);
}
/**

View File

@@ -6,7 +6,6 @@
(click)="open()">
<mat-icon [svgIcon]="'heroicons_outline:search'"></mat-icon>
</button>
<div
class="absolute inset-0 flex items-center flex-shrink-0 z-99 bg-card"
*ngIf="opened"
@@ -23,22 +22,36 @@
(keydown)="onKeydown($event)"
#barSearchInput>
<mat-autocomplete
class="max-h-128 border-t rounded-b shadow-md"
class="max-h-128 sm:px-2 border-t rounded-b shadow-md"
[disableRipple]="true"
#matAutocomplete="matAutocomplete">
<mat-option
class="h-14 px-6 py-0 sm:px-8 text-md pointer-events-none text-secondary bg-transparent"
*ngIf="results && !results.length">
class="py-0 px-6 text-md pointer-events-none text-secondary bg-transparent"
*ngIf="resultSets && !resultSets.length">
No results found!
</mat-option>
<ng-container *ngFor="let result of results; trackBy: trackByFn">
<mat-option
class="group relative h-14 px-6 py-0 sm:px-8 text-md"
[routerLink]="result.link">
<ng-container
[ngTemplateOutlet]="searchResult"
[ngTemplateOutletContext]="{$implicit: result}"></ng-container>
</mat-option>
<ng-container *ngFor="let resultSet of resultSets; trackBy: trackByFn">
<mat-optgroup class="flex items-center mt-2 px-2">
<span class="text-sm font-semibold tracking-wider text-secondary">{{resultSet.label.toUpperCase()}}</span>
</mat-optgroup>
<ng-container *ngFor="let result of resultSet.results; trackBy: trackByFn">
<mat-option
class="group relative mb-1 py-0 px-6 text-md rounded-md hover:bg-gray-100 dark:hover:bg-hover"
[routerLink]="result.link">
<!-- Contacts -->
<ng-container *ngIf="resultSet.id === 'contacts'">
<ng-container *ngTemplateOutlet="contactResult; context: {$implicit: result}"></ng-container>
</ng-container>
<!-- Pages -->
<ng-container *ngIf="resultSet.id === 'pages'">
<ng-container *ngTemplateOutlet="pageResult; context: {$implicit: result}"></ng-container>
</ng-container>
<!-- Tasks -->
<ng-container *ngIf="resultSet.id === 'tasks'">
<ng-container *ngTemplateOutlet="taskResult; context: {$implicit: result}"></ng-container>
</ng-container>
</mat-option>
</ng-container>
</ng-container>
</mat-autocomplete>
<button
@@ -69,62 +82,89 @@
[disableRipple]="true"
#matAutocomplete="matAutocomplete">
<mat-option
class="h-14 px-5 py-0 text-md pointer-events-none text-secondary bg-transparent"
*ngIf="results && !results.length">
class="py-0 px-6 text-md pointer-events-none text-secondary bg-transparent"
*ngIf="resultSets && !resultSets.length">
No results found!
</mat-option>
<ng-container *ngFor="let result of results; trackBy: trackByFn">
<mat-option
class="group relative h-14 px-5 py-0 text-md"
[routerLink]="result.link">
<ng-container
[ngTemplateOutlet]="searchResult"
[ngTemplateOutletContext]="{$implicit: result}"></ng-container>
</mat-option>
<ng-container *ngFor="let resultSet of resultSets; trackBy: trackByFn">
<mat-optgroup class="flex items-center mt-2 px-2">
<span class="text-sm font-semibold tracking-wider text-secondary">{{resultSet.label.toUpperCase()}}</span>
</mat-optgroup>
<ng-container *ngFor="let result of resultSet.results; trackBy: trackByFn">
<mat-option
class="group relative mb-1 py-0 px-6 text-md rounded-md hover:bg-gray-100 dark:hover:bg-hover"
[routerLink]="result.link">
<!-- Contacts -->
<ng-container *ngIf="resultSet.id === 'contacts'">
<ng-container *ngTemplateOutlet="contactResult; context: {$implicit: result}"></ng-container>
</ng-container>
<!-- Pages -->
<ng-container *ngIf="resultSet.id === 'pages'">
<ng-container *ngTemplateOutlet="pageResult; context: {$implicit: result}"></ng-container>
</ng-container>
<!-- Tasks -->
<ng-container *ngIf="resultSet.id === 'tasks'">
<ng-container *ngTemplateOutlet="taskResult; context: {$implicit: result}"></ng-container>
</ng-container>
</mat-option>
</ng-container>
</ng-container>
</mat-autocomplete>
</div>
</ng-container>
<!-- Contact result template -->
<ng-template
#searchResult
#contactResult
let-result>
<!-- Hover indicator -->
<div class="absolute inset-y-0 left-0 hidden w-1 bg-primary group-hover:flex"></div>
<!-- Contact result -->
<ng-container *ngIf="result.resultType === 'contact'">
<div class="flex items-center">
<div class="px-1.5 py-1 mr-4 text-xs font-semibold leading-normal rounded text-indigo-50 bg-indigo-600">Contact</div>
<div class="overflow-hidden overflow-ellipsis">
<span [innerHTML]="result.title"></span>
</div>
<div class="flex flex-shrink-0 items-center justify-center w-8 h-8 ml-auto rounded-full overflow-hidden bg-primary-100 dark:bg-black dark:bg-opacity-5">
<img
*ngIf="result.avatar"
[src]="result.avatar">
<mat-icon
class="m-0 icon-size-5 text-primary"
*ngIf="!result.avatar"
[svgIcon]="'heroicons_outline:user-circle'"></mat-icon>
</div>
<div class="flex items-center">
<div class="flex flex-shrink-0 items-center justify-center w-8 h-8 rounded-full overflow-hidden bg-primary-100 dark:bg-primary-800">
<img
*ngIf="result.avatar"
[src]="result.avatar">
<mat-icon
class="m-0 icon-size-5 text-primary dark:text-primary-400"
*ngIf="!result.avatar"
[svgIcon]="'heroicons_outline:user-circle'"></mat-icon>
</div>
</ng-container>
<!-- Page result -->
<ng-container *ngIf="result.resultType === 'page'">
<div class="flex items-center">
<div class="px-1.5 py-1 mr-4 text-xs font-semibold leading-normal rounded text-teal-50 bg-teal-600">Page</div>
<div class="flex flex-col overflow-hidden overflow-ellipsis">
<span
class="overflow-hidden overflow-ellipsis whitespace-nowrap leading-normal"
[innerHTML]="result.title"></span>
<span
class="mt-1 text-secondary overflow-hidden overflow-ellipsis whitespace-nowrap leading-normal text-sm no-underline"
[routerLink]="result.link">{{result.link}}</span>
</div>
<div class="ml-3 truncate">
<span [innerHTML]="result.name"></span>
</div>
</ng-container>
</div>
</ng-template>
<!-- Page result template -->
<ng-template
#pageResult
let-result>
<div class="flex flex-col">
<div
class="truncate leading-normal"
[innerHTML]="result.title"></div>
<div class="truncate leading-normal text-sm text-secondary">
{{result.link}}
</div>
</div>
</ng-template>
<!-- Task result template -->
<ng-template
#taskResult
let-result>
<div class="flex items-center">
<ng-container *ngIf="result.completed">
<mat-icon
class="mr-0 text-primary dark:text-primary-400"
[svgIcon]="'heroicons_outline:check-circle'"></mat-icon>
</ng-container>
<ng-container *ngIf="!result.completed">
<mat-icon
class="mr-0 text-hint"
[svgIcon]="'heroicons_outline:check-circle'"></mat-icon>
</ng-container>
<div
class="ml-3 truncate leading-normal"
[ngClass]="{'line-through text-hint': result.completed}"
[innerHTML]="result.title"></div>
</div>
</ng-template>

View File

@@ -20,7 +20,7 @@ export class SearchComponent implements OnChanges, OnInit, OnDestroy
@Output() search: EventEmitter<any> = new EventEmitter<any>();
opened: boolean = false;
results: any[];
resultSets: any[];
searchControl: FormControl = new FormControl();
private _unsubscribeAll: Subject<any> = new Subject<any>();
@@ -104,12 +104,12 @@ export class SearchComponent implements OnChanges, OnInit, OnDestroy
takeUntil(this._unsubscribeAll),
map((value) => {
// Set the search results to null if there is no value or
// Set the resultSets to null if there is no value or
// the length of the value is smaller than the minLength
// so the autocomplete panel can be closed
if ( !value || value.length < this.minLength )
{
this.results = null;
this.resultSets = null;
}
// Continue
@@ -121,13 +121,13 @@ export class SearchComponent implements OnChanges, OnInit, OnDestroy
)
.subscribe((value) => {
this._httpClient.post('api/common/search', {query: value})
.subscribe((response: any) => {
.subscribe((resultSets: any) => {
// Store the results
this.results = response.results;
// Store the result sets
this.resultSets = resultSets;
// Execute the event
this.search.next(this.results);
this.search.next(resultSets);
});
});
}

View File

@@ -21,7 +21,15 @@
[svgIcon]="'heroicons_solid:x'"></mat-icon>
</button>
</div>
<div class="text-lg font-medium leading-10">Shortcuts</div>
<div class="flex items-center text-lg font-medium leading-10">
<span class="">Shortcuts</span>
<ng-container *ngIf="mode !== 'view'">
<span class="ml-1">
<ng-container *ngIf="mode === 'add'">- Add new</ng-container>
<ng-container *ngIf="mode === 'modify' || mode === 'edit'">- Editing</ng-container>
</span>
</ng-container>
</div>
<div class="ml-auto">
<!-- View mode -->

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, TemplateRef, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, TemplateRef, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Overlay, OverlayRef } from '@angular/cdk/overlay';
import { TemplatePortal } from '@angular/cdk/portal';
@@ -15,14 +15,14 @@ import { ShortcutsService } from 'app/layout/common/shortcuts/shortcuts.service'
changeDetection: ChangeDetectionStrategy.OnPush,
exportAs : 'shortcuts'
})
export class ShortcutsComponent implements OnChanges, OnInit, OnDestroy
export class ShortcutsComponent implements OnInit, OnDestroy
{
@Input() shortcuts: Shortcut[];
@ViewChild('shortcutsOrigin') private _shortcutsOrigin: MatButton;
@ViewChild('shortcutsPanel') private _shortcutsPanel: TemplateRef<any>;
mode: 'view' | 'modify' | 'add' | 'edit' = 'view';
shortcutForm: FormGroup;
shortcuts: Shortcut[];
private _overlayRef: OverlayRef;
private _unsubscribeAll: Subject<any> = new Subject<any>();
@@ -43,21 +43,6 @@ export class ShortcutsComponent implements OnChanges, OnInit, OnDestroy
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On changes
*
* @param changes
*/
ngOnChanges(changes: SimpleChanges): void
{
// Shortcuts
if ( 'shortcuts' in changes )
{
// Store the shortcuts on the service
this._shortcutsService.store(changes.shortcuts.currentValue);
}
}
/**
* On init
*/
@@ -236,7 +221,7 @@ export class ShortcutsComponent implements OnChanges, OnInit, OnDestroy
scrollStrategy : this._overlay.scrollStrategies.block(),
positionStrategy: this._overlay.position()
.flexibleConnectedTo(this._shortcutsOrigin._elementRef.nativeElement)
.withLockedPosition()
.withLockedPosition(true)
.withPush(true)
.withPositions([
{

View File

@@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, ReplaySubject } from 'rxjs';
import { map, switchMap, take } from 'rxjs/operators';
import { map, switchMap, take, tap } from 'rxjs/operators';
import { Shortcut } from 'app/layout/common/shortcuts/shortcuts.types';
@Injectable({
@@ -35,17 +35,15 @@ export class ShortcutsService
// -----------------------------------------------------------------------------------------------------
/**
* Store shortcuts on the service
*
* @param shortcuts
* Get all messages
*/
store(shortcuts: Shortcut[]): Observable<Shortcut[]>
getAll(): Observable<Shortcut[]>
{
// Load the shortcuts
this._shortcuts.next(shortcuts);
// Return the shortcuts
return this.shortcuts$;
return this._httpClient.get<Shortcut[]>('api/common/shortcuts').pipe(
tap((shortcuts) => {
this._shortcuts.next(shortcuts);
})
);
}
/**

View File

@@ -3,17 +3,17 @@ import { Router } from '@angular/router';
import { BooleanInput } from '@angular/cdk/coercion';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { User } from 'app/core/user/user.model';
import { User } from 'app/core/user/user.types';
import { UserService } from 'app/core/user/user.service';
@Component({
selector : 'user-menu',
templateUrl : './user-menu.component.html',
selector : 'user',
templateUrl : './user.component.html',
encapsulation : ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
exportAs : 'userMenu'
exportAs : 'user'
})
export class UserMenuComponent implements OnInit, OnDestroy
export class UserComponent implements OnInit, OnDestroy
{
/* eslint-disable @typescript-eslint/naming-convention */
static ngAcceptInputType_showAvatar: BooleanInput;

View File

@@ -3,12 +3,12 @@ import { MatButtonModule } from '@angular/material/button';
import { MatDividerModule } from '@angular/material/divider';
import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { UserMenuComponent } from 'app/layout/common/user-menu/user-menu.component';
import { UserComponent } from 'app/layout/common/user/user.component';
import { SharedModule } from 'app/shared/shared.module';
@NgModule({
declarations: [
UserMenuComponent
UserComponent
],
imports : [
MatButtonModule,
@@ -18,9 +18,9 @@ import { SharedModule } from 'app/shared/shared.module';
SharedModule
],
exports : [
UserMenuComponent
UserComponent
]
})
export class UserMenuModule
export class UserModule
{
}

View File

@@ -54,7 +54,7 @@
</div>
<fuse-drawer
class="w-100 min-w-100"
class="w-screen min-w-screen sm:w-100 sm:min-w-100"
fixed
transparentOverlay
[mode]="'over'"
@@ -74,17 +74,17 @@
<!-- Theme -->
<div class="text-md font-semibold text-secondary">THEME</div>
<div class="grid grid-cols-3 gap-3 mt-6">
<div class="grid grid-cols-2 sm:grid-cols-3 gap-3 mt-6">
<ng-container *ngFor="let theme of themes">
<div
class="flex items-center px-4 py-3 rounded-full cursor-pointer ring-inset ring-primary bg-hover"
class="flex items-center justify-center px-4 py-3 rounded-full cursor-pointer ring-inset ring-primary bg-hover"
[class.ring-2]="config.theme === theme[0]"
(click)="setTheme(theme[0])">
<div
class="w-4 h-4 rounded-full"
class="flex-0 w-3 h-3 rounded-full"
[style.background-color]="theme[1].primary"></div>
<div
class="ml-2.5 font-medium leading-5"
class="ml-2.5 font-medium leading-5 truncate"
[class.text-secondary]="config.theme !== theme[0]">
{{theme[0] | titlecase}}
</div>

View File

@@ -6,7 +6,7 @@
class="dark bg-gray-900 print:hidden"
[mode]="'over'"
[name]="'mainNavigation'"
[navigation]="data.navigation.default"
[navigation]="navigation.default"
[opened]="false">
<!-- Navigation header hook -->
<ng-container fuseVerticalNavigationContentHeader>
@@ -50,7 +50,7 @@
<fuse-horizontal-navigation
class="mr-2"
[name]="'mainNavigation'"
[navigation]="data.navigation.horizontal"></fuse-horizontal-navigation>
[navigation]="navigation.horizontal"></fuse-horizontal-navigation>
</ng-container>
<!-- Navigation toggle button -->
<ng-container *ngIf="isScreenSmall">
@@ -62,14 +62,14 @@
</button>
</ng-container>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<language></language>
<fuse-fullscreen></fuse-fullscreen>
<div class="flex items-center pl-2 ml-auto space-x-1 sm:space-x-2">
<languages></languages>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts [shortcuts]="data.shortcuts"></shortcuts>
<messages [messages]="data.messages"></messages>
<notifications [notifications]="data.notifications"></notifications>
<user-menu></user-menu>
<shortcuts></shortcuts>
<messages></messages>
<notifications></notifications>
<user></user>
</div>
</div>

View File

@@ -4,7 +4,8 @@ import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation';
import { InitialData } from 'app/app.types';
import { Navigation } from 'app/core/navigation/navigation.types';
import { NavigationService } from 'app/core/navigation/navigation.service';
@Component({
selector : 'centered-layout',
@@ -13,7 +14,7 @@ import { InitialData } from 'app/app.types';
})
export class CenteredLayoutComponent implements OnInit, OnDestroy
{
data: InitialData;
navigation: Navigation;
isScreenSmall: boolean;
private _unsubscribeAll: Subject<any> = new Subject<any>();
@@ -23,6 +24,7 @@ export class CenteredLayoutComponent implements OnInit, OnDestroy
constructor(
private _activatedRoute: ActivatedRoute,
private _router: Router,
private _navigationService: NavigationService,
private _fuseMediaWatcherService: FuseMediaWatcherService,
private _fuseNavigationService: FuseNavigationService
)
@@ -50,10 +52,12 @@ export class CenteredLayoutComponent implements OnInit, OnDestroy
*/
ngOnInit(): void
{
// Subscribe to the resolved route data
this._activatedRoute.data.subscribe((data: Data) => {
this.data = data.initialData;
});
// Subscribe to navigation data
this._navigationService.navigation$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((navigation: Navigation) => {
this.navigation = navigation;
});
// Subscribe to media changes
this._fuseMediaWatcherService.onMediaChange$

View File

@@ -7,12 +7,12 @@ import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { FuseFullscreenModule } from '@fuse/components/fullscreen';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { LanguageModule } from 'app/layout/common/language/language.module';
import { LanguagesModule } from 'app/layout/common/languages/languages.module';
import { MessagesModule } from 'app/layout/common/messages/messages.module';
import { NotificationsModule } from 'app/layout/common/notifications/notifications.module';
import { SearchModule } from 'app/layout/common/search/search.module';
import { ShortcutsModule } from 'app/layout/common/shortcuts/shortcuts.module';
import { UserMenuModule } from 'app/layout/common/user-menu/user-menu.module';
import { UserModule } from 'app/layout/common/user/user.module';
import { SharedModule } from 'app/shared/shared.module';
import { CenteredLayoutComponent } from 'app/layout/layouts/horizontal/centered/centered.component';
@@ -29,12 +29,12 @@ import { CenteredLayoutComponent } from 'app/layout/layouts/horizontal/centered/
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
LanguageModule,
LanguagesModule,
MessagesModule,
NotificationsModule,
SearchModule,
ShortcutsModule,
UserMenuModule,
UserModule,
SharedModule
],
exports : [

View File

@@ -4,7 +4,7 @@
class="dark bg-gray-900 print:hidden"
[mode]="'over'"
[name]="'mainNavigation'"
[navigation]="data.navigation.default"
[navigation]="navigation.default"
[opened]="false">
<!-- Navigation header hook -->
<ng-container fuseVerticalNavigationContentHeader>
@@ -45,14 +45,14 @@
</button>
</ng-container>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<language></language>
<fuse-fullscreen></fuse-fullscreen>
<div class="flex items-center pl-2 ml-auto space-x-1 sm:space-x-2">
<languages></languages>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts [shortcuts]="data.shortcuts"></shortcuts>
<messages [messages]="data.messages"></messages>
<notifications [notifications]="data.notifications"></notifications>
<user-menu></user-menu>
<shortcuts></shortcuts>
<messages></messages>
<notifications></notifications>
<user></user>
</div>
</div>
</div>
@@ -63,7 +63,7 @@
<fuse-horizontal-navigation
class="-mx-4"
[name]="'mainNavigation'"
[navigation]="data.navigation.horizontal"></fuse-horizontal-navigation>
[navigation]="navigation.horizontal"></fuse-horizontal-navigation>
</div>
</div>
</ng-container>

View File

@@ -1,10 +1,11 @@
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { ActivatedRoute, Data, Router } from '@angular/router';
import { ActivatedRoute, Router } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation';
import { InitialData } from 'app/app.types';
import { Navigation } from 'app/core/navigation/navigation.types';
import { NavigationService } from 'app/core/navigation/navigation.service';
@Component({
selector : 'enterprise-layout',
@@ -13,8 +14,8 @@ import { InitialData } from 'app/app.types';
})
export class EnterpriseLayoutComponent implements OnInit, OnDestroy
{
data: InitialData;
isScreenSmall: boolean;
navigation: Navigation;
private _unsubscribeAll: Subject<any> = new Subject<any>();
/**
@@ -23,6 +24,7 @@ export class EnterpriseLayoutComponent implements OnInit, OnDestroy
constructor(
private _activatedRoute: ActivatedRoute,
private _router: Router,
private _navigationService: NavigationService,
private _fuseMediaWatcherService: FuseMediaWatcherService,
private _fuseNavigationService: FuseNavigationService
)
@@ -50,10 +52,12 @@ export class EnterpriseLayoutComponent implements OnInit, OnDestroy
*/
ngOnInit(): void
{
// Subscribe to the resolved route data
this._activatedRoute.data.subscribe((data: Data) => {
this.data = data.initialData;
});
// Subscribe to navigation data
this._navigationService.navigation$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((navigation: Navigation) => {
this.navigation = navigation;
});
// Subscribe to media changes
this._fuseMediaWatcherService.onMediaChange$

View File

@@ -7,12 +7,12 @@ import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { FuseFullscreenModule } from '@fuse/components/fullscreen';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { LanguageModule } from 'app/layout/common/language/language.module';
import { LanguagesModule } from 'app/layout/common/languages/languages.module';
import { MessagesModule } from 'app/layout/common/messages/messages.module';
import { NotificationsModule } from 'app/layout/common/notifications/notifications.module';
import { SearchModule } from 'app/layout/common/search/search.module';
import { ShortcutsModule } from 'app/layout/common/shortcuts/shortcuts.module';
import { UserMenuModule } from 'app/layout/common/user-menu/user-menu.module';
import { UserModule } from 'app/layout/common/user/user.module';
import { SharedModule } from 'app/shared/shared.module';
import { EnterpriseLayoutComponent } from 'app/layout/layouts/horizontal/enterprise/enterprise.component';
@@ -29,12 +29,12 @@ import { EnterpriseLayoutComponent } from 'app/layout/layouts/horizontal/enterpr
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
LanguageModule,
LanguagesModule,
MessagesModule,
NotificationsModule,
SearchModule,
ShortcutsModule,
UserMenuModule,
UserModule,
SharedModule
],
exports : [

View File

@@ -4,7 +4,7 @@
class="dark bg-gray-900 print:hidden"
[mode]="'over'"
[name]="'mainNavigation'"
[navigation]="data.navigation.default"
[navigation]="navigation.default"
[opened]="false">
<!-- Navigation header hook -->
<ng-container fuseVerticalNavigationContentHeader>
@@ -51,14 +51,14 @@
</button>
</ng-container>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<language></language>
<fuse-fullscreen></fuse-fullscreen>
<div class="flex items-center pl-2 ml-auto space-x-1 sm:space-x-2">
<languages></languages>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts [shortcuts]="data.shortcuts"></shortcuts>
<messages [messages]="data.messages"></messages>
<notifications [notifications]="data.notifications"></notifications>
<user-menu></user-menu>
<shortcuts></shortcuts>
<messages></messages>
<notifications></notifications>
<user></user>
</div>
</div>
<!-- Bottom bar -->
@@ -66,7 +66,7 @@
<div class="relative flex flex-auto flex-0 items-center h-16 px-4">
<fuse-horizontal-navigation
[name]="'mainNavigation'"
[navigation]="data.navigation.horizontal"></fuse-horizontal-navigation>
[navigation]="navigation.horizontal"></fuse-horizontal-navigation>
</div>
</ng-container>
</div>

View File

@@ -1,10 +1,11 @@
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { ActivatedRoute, Data, Router } from '@angular/router';
import { ActivatedRoute, Router } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation';
import { InitialData } from 'app/app.types';
import { Navigation } from 'app/core/navigation/navigation.types';
import { NavigationService } from 'app/core/navigation/navigation.service';
@Component({
selector : 'material-layout',
@@ -13,8 +14,8 @@ import { InitialData } from 'app/app.types';
})
export class MaterialLayoutComponent implements OnInit, OnDestroy
{
data: InitialData;
isScreenSmall: boolean;
navigation: Navigation;
private _unsubscribeAll: Subject<any> = new Subject<any>();
/**
@@ -23,6 +24,7 @@ export class MaterialLayoutComponent implements OnInit, OnDestroy
constructor(
private _activatedRoute: ActivatedRoute,
private _router: Router,
private _navigationService: NavigationService,
private _fuseMediaWatcherService: FuseMediaWatcherService,
private _fuseNavigationService: FuseNavigationService
)
@@ -50,10 +52,12 @@ export class MaterialLayoutComponent implements OnInit, OnDestroy
*/
ngOnInit(): void
{
// Subscribe to the resolved route data
this._activatedRoute.data.subscribe((data: Data) => {
this.data = data.initialData;
});
// Subscribe to navigation data
this._navigationService.navigation$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((navigation: Navigation) => {
this.navigation = navigation;
});
// Subscribe to media changes
this._fuseMediaWatcherService.onMediaChange$

View File

@@ -7,12 +7,12 @@ import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { FuseFullscreenModule } from '@fuse/components/fullscreen';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { LanguageModule } from 'app/layout/common/language/language.module';
import { LanguagesModule } from 'app/layout/common/languages/languages.module';
import { MessagesModule } from 'app/layout/common/messages/messages.module';
import { NotificationsModule } from 'app/layout/common/notifications/notifications.module';
import { SearchModule } from 'app/layout/common/search/search.module';
import { ShortcutsModule } from 'app/layout/common/shortcuts/shortcuts.module';
import { UserMenuModule } from 'app/layout/common/user-menu/user-menu.module';
import { UserModule } from 'app/layout/common/user/user.module';
import { SharedModule } from 'app/shared/shared.module';
import { MaterialLayoutComponent } from 'app/layout/layouts/horizontal/material/material.component';
@@ -29,12 +29,12 @@ import { MaterialLayoutComponent } from 'app/layout/layouts/horizontal/material/
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
LanguageModule,
LanguagesModule,
MessagesModule,
NotificationsModule,
SearchModule,
ShortcutsModule,
UserMenuModule,
UserModule,
SharedModule
],
exports : [

View File

@@ -4,7 +4,7 @@
class="dark bg-gray-900 print:hidden"
[mode]="'over'"
[name]="'mainNavigation'"
[navigation]="data.navigation.default"
[navigation]="navigation.default"
[opened]="false">
<!-- Navigation header hook -->
<ng-container fuseVerticalNavigationContentHeader>
@@ -43,7 +43,7 @@
<fuse-horizontal-navigation
class="mr-2"
[name]="'mainNavigation'"
[navigation]="data.navigation.horizontal"></fuse-horizontal-navigation>
[navigation]="navigation.horizontal"></fuse-horizontal-navigation>
</ng-container>
<!-- Navigation toggle button -->
<ng-container *ngIf="isScreenSmall">
@@ -54,14 +54,14 @@
</button>
</ng-container>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<language></language>
<fuse-fullscreen></fuse-fullscreen>
<div class="flex items-center pl-2 ml-auto space-x-1 sm:space-x-2">
<languages></languages>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts [shortcuts]="data.shortcuts"></shortcuts>
<messages [messages]="data.messages"></messages>
<notifications [notifications]="data.notifications"></notifications>
<user-menu></user-menu>
<shortcuts></shortcuts>
<messages></messages>
<notifications></notifications>
<user></user>
</div>
</div>

View File

@@ -1,10 +1,11 @@
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { ActivatedRoute, Data, Router } from '@angular/router';
import { ActivatedRoute, Router } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation';
import { InitialData } from 'app/app.types';
import { Navigation } from 'app/core/navigation/navigation.types';
import { NavigationService } from 'app/core/navigation/navigation.service';
@Component({
selector : 'modern-layout',
@@ -13,8 +14,8 @@ import { InitialData } from 'app/app.types';
})
export class ModernLayoutComponent implements OnInit, OnDestroy
{
data: InitialData;
isScreenSmall: boolean;
navigation: Navigation;
private _unsubscribeAll: Subject<any> = new Subject<any>();
/**
@@ -23,6 +24,7 @@ export class ModernLayoutComponent implements OnInit, OnDestroy
constructor(
private _activatedRoute: ActivatedRoute,
private _router: Router,
private _navigationService: NavigationService,
private _fuseMediaWatcherService: FuseMediaWatcherService,
private _fuseNavigationService: FuseNavigationService
)
@@ -50,10 +52,12 @@ export class ModernLayoutComponent implements OnInit, OnDestroy
*/
ngOnInit(): void
{
// Subscribe to the resolved route data
this._activatedRoute.data.subscribe((data: Data) => {
this.data = data.initialData;
});
// Subscribe to navigation data
this._navigationService.navigation$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((navigation: Navigation) => {
this.navigation = navigation;
});
// Subscribe to media changes
this._fuseMediaWatcherService.onMediaChange$

View File

@@ -7,12 +7,12 @@ import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { FuseFullscreenModule } from '@fuse/components/fullscreen';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { LanguageModule } from 'app/layout/common/language/language.module';
import { LanguagesModule } from 'app/layout/common/languages/languages.module';
import { MessagesModule } from 'app/layout/common/messages/messages.module';
import { NotificationsModule } from 'app/layout/common/notifications/notifications.module';
import { SearchModule } from 'app/layout/common/search/search.module';
import { ShortcutsModule } from 'app/layout/common/shortcuts/shortcuts.module';
import { UserMenuModule } from 'app/layout/common/user-menu/user-menu.module';
import { UserModule } from 'app/layout/common/user/user.module';
import { SharedModule } from 'app/shared/shared.module';
import { ModernLayoutComponent } from 'app/layout/layouts/horizontal/modern/modern.component';
@@ -29,12 +29,12 @@ import { ModernLayoutComponent } from 'app/layout/layouts/horizontal/modern/mode
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
LanguageModule,
LanguagesModule,
MessagesModule,
NotificationsModule,
SearchModule,
ShortcutsModule,
UserMenuModule,
UserModule,
SharedModule
],
exports : [

View File

@@ -3,7 +3,7 @@
class="dark bg-gray-900 print:hidden"
[mode]="isScreenSmall ? 'over' : 'side'"
[name]="'mainNavigation'"
[navigation]="data.navigation.default"
[navigation]="navigation.default"
[opened]="!isScreenSmall">
<!-- Navigation header hook -->
<ng-container fuseVerticalNavigationContentHeader>
@@ -35,14 +35,14 @@
<mat-icon [svgIcon]="'heroicons_outline:menu'"></mat-icon>
</button>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<language></language>
<fuse-fullscreen></fuse-fullscreen>
<div class="flex items-center pl-2 ml-auto space-x-1 sm:space-x-2">
<languages></languages>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts [shortcuts]="data.shortcuts"></shortcuts>
<messages [messages]="data.messages"></messages>
<notifications [notifications]="data.notifications"></notifications>
<user-menu></user-menu>
<shortcuts></shortcuts>
<messages></messages>
<notifications></notifications>
<user></user>
</div>
</div>

View File

@@ -1,10 +1,11 @@
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { ActivatedRoute, Data, Router } from '@angular/router';
import { ActivatedRoute, Router } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation';
import { InitialData } from 'app/app.types';
import { Navigation } from 'app/core/navigation/navigation.types';
import { NavigationService } from 'app/core/navigation/navigation.service';
@Component({
selector : 'classic-layout',
@@ -13,8 +14,8 @@ import { InitialData } from 'app/app.types';
})
export class ClassicLayoutComponent implements OnInit, OnDestroy
{
data: InitialData;
isScreenSmall: boolean;
navigation: Navigation;
private _unsubscribeAll: Subject<any> = new Subject<any>();
/**
@@ -23,6 +24,7 @@ export class ClassicLayoutComponent implements OnInit, OnDestroy
constructor(
private _activatedRoute: ActivatedRoute,
private _router: Router,
private _navigationService: NavigationService,
private _fuseMediaWatcherService: FuseMediaWatcherService,
private _fuseNavigationService: FuseNavigationService
)
@@ -50,10 +52,12 @@ export class ClassicLayoutComponent implements OnInit, OnDestroy
*/
ngOnInit(): void
{
// Subscribe to the resolved route data
this._activatedRoute.data.subscribe((data: Data) => {
this.data = data.initialData;
});
// Subscribe to navigation data
this._navigationService.navigation$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((navigation: Navigation) => {
this.navigation = navigation;
});
// Subscribe to media changes
this._fuseMediaWatcherService.onMediaChange$

View File

@@ -7,12 +7,12 @@ import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { FuseFullscreenModule } from '@fuse/components/fullscreen';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { LanguageModule } from 'app/layout/common/language/language.module';
import { LanguagesModule } from 'app/layout/common/languages/languages.module';
import { MessagesModule } from 'app/layout/common/messages/messages.module';
import { NotificationsModule } from 'app/layout/common/notifications/notifications.module';
import { SearchModule } from 'app/layout/common/search/search.module';
import { ShortcutsModule } from 'app/layout/common/shortcuts/shortcuts.module';
import { UserMenuModule } from 'app/layout/common/user-menu/user-menu.module';
import { UserModule } from 'app/layout/common/user/user.module';
import { SharedModule } from 'app/shared/shared.module';
import { ClassicLayoutComponent } from 'app/layout/layouts/vertical/classic/classic.component';
@@ -29,12 +29,12 @@ import { ClassicLayoutComponent } from 'app/layout/layouts/vertical/classic/clas
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
LanguageModule,
LanguagesModule,
MessagesModule,
NotificationsModule,
SearchModule,
ShortcutsModule,
UserMenuModule,
UserModule,
SharedModule
],
exports : [

View File

@@ -3,7 +3,7 @@
class="dark bg-gray-900 print:hidden"
[mode]="isScreenSmall ? 'over' : 'side'"
[name]="'mainNavigation'"
[navigation]="data.navigation.default"
[navigation]="navigation.default"
[opened]="!isScreenSmall">
<!-- Navigation header hook -->
<ng-container fuseVerticalNavigationContentHeader>
@@ -16,8 +16,8 @@
</div>
<!-- Components -->
<div class="flex items-center ml-auto">
<notifications [notifications]="data.notifications"></notifications>
<user-menu [showAvatar]="false"></user-menu>
<notifications></notifications>
<user [showAvatar]="false"></user>
</div>
</div>
<!-- User -->
@@ -25,20 +25,20 @@
<div class="relative w-24 h-24">
<img
class="w-full h-full rounded-full"
*ngIf="data.user.avatar"
[src]="data.user.avatar"
*ngIf="user.avatar"
[src]="user.avatar"
alt="User avatar">
<mat-icon
class="icon-size-24"
*ngIf="!data.user.avatar"
*ngIf="!user.avatar"
[svgIcon]="'heroicons_solid:user-circle'"></mat-icon>
</div>
<div class="flex flex-col items-center justify-center w-full mt-6">
<div class="w-full whitespace-nowrap overflow-ellipsis overflow-hidden text-center leading-normal font-medium">
{{data.user.name}}
{{user.name}}
</div>
<div class="w-full mt-0.5 whitespace-nowrap overflow-ellipsis overflow-hidden text-center text-md leading-normal font-medium text-secondary">
{{data.user.email}}
{{user.email}}
</div>
</div>
</div>
@@ -65,12 +65,12 @@
<mat-icon [svgIcon]="'heroicons_outline:menu'"></mat-icon>
</button>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<language></language>
<fuse-fullscreen></fuse-fullscreen>
<div class="flex items-center pl-2 ml-auto space-x-1 sm:space-x-2">
<languages></languages>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts [shortcuts]="data.shortcuts"></shortcuts>
<messages [messages]="data.messages"></messages>
<shortcuts></shortcuts>
<messages></messages>
</div>
</div>

View File

@@ -1,10 +1,13 @@
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { ActivatedRoute, Data, Router } from '@angular/router';
import { ActivatedRoute, Router } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation';
import { InitialData } from 'app/app.types';
import { Navigation } from 'app/core/navigation/navigation.types';
import { NavigationService } from 'app/core/navigation/navigation.service';
import { User } from 'app/core/user/user.types';
import { UserService } from 'app/core/user/user.service';
@Component({
selector : 'classy-layout',
@@ -13,8 +16,9 @@ import { InitialData } from 'app/app.types';
})
export class ClassyLayoutComponent implements OnInit, OnDestroy
{
data: InitialData;
isScreenSmall: boolean;
navigation: Navigation;
user: User;
private _unsubscribeAll: Subject<any> = new Subject<any>();
/**
@@ -23,6 +27,8 @@ export class ClassyLayoutComponent implements OnInit, OnDestroy
constructor(
private _activatedRoute: ActivatedRoute,
private _router: Router,
private _navigationService: NavigationService,
private _userService: UserService,
private _fuseMediaWatcherService: FuseMediaWatcherService,
private _fuseNavigationService: FuseNavigationService
)
@@ -50,10 +56,19 @@ export class ClassyLayoutComponent implements OnInit, OnDestroy
*/
ngOnInit(): void
{
// Subscribe to the resolved route data
this._activatedRoute.data.subscribe((data: Data) => {
this.data = data.initialData;
});
// Subscribe to navigation data
this._navigationService.navigation$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((navigation: Navigation) => {
this.navigation = navigation;
});
// Subscribe to the user service
this._userService.user$
.pipe((takeUntil(this._unsubscribeAll)))
.subscribe((user: User) => {
this.user = user;
});
// Subscribe to media changes
this._fuseMediaWatcherService.onMediaChange$

View File

@@ -7,12 +7,12 @@ import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { FuseFullscreenModule } from '@fuse/components/fullscreen/fullscreen.module';
import { LanguageModule } from 'app/layout/common/language/language.module';
import { LanguagesModule } from 'app/layout/common/languages/languages.module';
import { MessagesModule } from 'app/layout/common/messages/messages.module';
import { NotificationsModule } from 'app/layout/common/notifications/notifications.module';
import { SearchModule } from 'app/layout/common/search/search.module';
import { ShortcutsModule } from 'app/layout/common/shortcuts/shortcuts.module';
import { UserMenuModule } from 'app/layout/common/user-menu/user-menu.module';
import { UserModule } from 'app/layout/common/user/user.module';
import { SharedModule } from 'app/shared/shared.module';
import { ClassyLayoutComponent } from 'app/layout/layouts/vertical/classy/classy.component';
@@ -29,12 +29,12 @@ import { ClassyLayoutComponent } from 'app/layout/layouts/vertical/classy/classy
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
LanguageModule,
LanguagesModule,
MessagesModule,
NotificationsModule,
SearchModule,
ShortcutsModule,
UserMenuModule,
UserModule,
SharedModule
],
exports : [

View File

@@ -4,7 +4,7 @@
[appearance]="'compact'"
[mode]="isScreenSmall ? 'over' : 'side'"
[name]="'mainNavigation'"
[navigation]="data.navigation.compact"
[navigation]="navigation.compact"
[opened]="!isScreenSmall">
<!-- Navigation header hook -->
<ng-container fuseVerticalNavigationContentHeader>
@@ -30,14 +30,14 @@
<mat-icon [svgIcon]="'heroicons_outline:menu'"></mat-icon>
</button>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<language></language>
<fuse-fullscreen></fuse-fullscreen>
<div class="flex items-center pl-2 ml-auto space-x-1 sm:space-x-2">
<languages></languages>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts [shortcuts]="data.shortcuts"></shortcuts>
<messages [messages]="data.messages"></messages>
<notifications [notifications]="data.notifications"></notifications>
<user-menu></user-menu>
<shortcuts></shortcuts>
<messages></messages>
<notifications></notifications>
<user></user>
</div>
</div>

View File

@@ -1,10 +1,11 @@
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { ActivatedRoute, Data, Router } from '@angular/router';
import { ActivatedRoute, Router } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation';
import { InitialData } from 'app/app.types';
import { Navigation } from 'app/core/navigation/navigation.types';
import { NavigationService } from 'app/core/navigation/navigation.service';
@Component({
selector : 'compact-layout',
@@ -13,8 +14,8 @@ import { InitialData } from 'app/app.types';
})
export class CompactLayoutComponent implements OnInit, OnDestroy
{
data: InitialData;
isScreenSmall: boolean;
navigation: Navigation;
private _unsubscribeAll: Subject<any> = new Subject<any>();
/**
@@ -23,6 +24,7 @@ export class CompactLayoutComponent implements OnInit, OnDestroy
constructor(
private _activatedRoute: ActivatedRoute,
private _router: Router,
private _navigationService: NavigationService,
private _fuseMediaWatcherService: FuseMediaWatcherService,
private _fuseNavigationService: FuseNavigationService
)
@@ -50,10 +52,12 @@ export class CompactLayoutComponent implements OnInit, OnDestroy
*/
ngOnInit(): void
{
// Subscribe to the resolved route data
this._activatedRoute.data.subscribe((data: Data) => {
this.data = data.initialData;
});
// Subscribe to navigation data
this._navigationService.navigation$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((navigation: Navigation) => {
this.navigation = navigation;
});
// Subscribe to media changes
this._fuseMediaWatcherService.onMediaChange$

View File

@@ -7,12 +7,12 @@ import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { FuseFullscreenModule } from '@fuse/components/fullscreen';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { LanguageModule } from 'app/layout/common/language/language.module';
import { LanguagesModule } from 'app/layout/common/languages/languages.module';
import { MessagesModule } from 'app/layout/common/messages/messages.module';
import { NotificationsModule } from 'app/layout/common/notifications/notifications.module';
import { SearchModule } from 'app/layout/common/search/search.module';
import { ShortcutsModule } from 'app/layout/common/shortcuts/shortcuts.module';
import { UserMenuModule } from 'app/layout/common/user-menu/user-menu.module';
import { UserModule } from 'app/layout/common/user/user.module';
import { SharedModule } from 'app/shared/shared.module';
import { CompactLayoutComponent } from 'app/layout/layouts/vertical/compact/compact.component';
@@ -29,12 +29,12 @@ import { CompactLayoutComponent } from 'app/layout/layouts/vertical/compact/comp
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
LanguageModule,
LanguagesModule,
MessagesModule,
NotificationsModule,
SearchModule,
ShortcutsModule,
UserMenuModule,
UserModule,
SharedModule
],
exports : [

View File

@@ -4,7 +4,7 @@
[appearance]="navigationAppearance"
[mode]="isScreenSmall ? 'over' : 'side'"
[name]="'mainNavigation'"
[navigation]="data.navigation.default"
[navigation]="navigation.default"
[opened]="!isScreenSmall">
<!-- Navigation header hook -->
<ng-container fuseVerticalNavigationContentHeader>
@@ -39,14 +39,14 @@
</button>
</div>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<language></language>
<fuse-fullscreen></fuse-fullscreen>
<div class="flex items-center pl-2 ml-auto space-x-1 sm:space-x-2">
<languages></languages>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts [shortcuts]="data.shortcuts"></shortcuts>
<messages [messages]="data.messages"></messages>
<notifications [notifications]="data.notifications"></notifications>
<user-menu></user-menu>
<shortcuts></shortcuts>
<messages></messages>
<notifications></notifications>
<user></user>
</div>
</div>

View File

@@ -1,10 +1,11 @@
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { ActivatedRoute, Data, Router } from '@angular/router';
import { ActivatedRoute, Router } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation';
import { InitialData } from 'app/app.types';
import { Navigation } from 'app/core/navigation/navigation.types';
import { NavigationService } from 'app/core/navigation/navigation.service';
@Component({
selector : 'dense-layout',
@@ -13,8 +14,8 @@ import { InitialData } from 'app/app.types';
})
export class DenseLayoutComponent implements OnInit, OnDestroy
{
data: InitialData;
isScreenSmall: boolean;
navigation: Navigation;
navigationAppearance: 'default' | 'dense' = 'dense';
private _unsubscribeAll: Subject<any> = new Subject<any>();
@@ -24,6 +25,7 @@ export class DenseLayoutComponent implements OnInit, OnDestroy
constructor(
private _activatedRoute: ActivatedRoute,
private _router: Router,
private _navigationService: NavigationService,
private _fuseMediaWatcherService: FuseMediaWatcherService,
private _fuseNavigationService: FuseNavigationService
)
@@ -51,10 +53,12 @@ export class DenseLayoutComponent implements OnInit, OnDestroy
*/
ngOnInit(): void
{
// Subscribe to the resolved route data
this._activatedRoute.data.subscribe((data: Data) => {
this.data = data.initialData;
});
// Subscribe to navigation data
this._navigationService.navigation$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((navigation: Navigation) => {
this.navigation = navigation;
});
// Subscribe to media changes
this._fuseMediaWatcherService.onMediaChange$

View File

@@ -7,12 +7,12 @@ import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { FuseFullscreenModule } from '@fuse/components/fullscreen';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { LanguageModule } from 'app/layout/common/language/language.module';
import { LanguagesModule } from 'app/layout/common/languages/languages.module';
import { MessagesModule } from 'app/layout/common/messages/messages.module';
import { NotificationsModule } from 'app/layout/common/notifications/notifications.module';
import { SearchModule } from 'app/layout/common/search/search.module';
import { ShortcutsModule } from 'app/layout/common/shortcuts/shortcuts.module';
import { UserMenuModule } from 'app/layout/common/user-menu/user-menu.module';
import { UserModule } from 'app/layout/common/user/user.module';
import { SharedModule } from 'app/shared/shared.module';
import { DenseLayoutComponent } from 'app/layout/layouts/vertical/dense/dense.component';
@@ -29,12 +29,12 @@ import { DenseLayoutComponent } from 'app/layout/layouts/vertical/dense/dense.co
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
LanguageModule,
LanguagesModule,
MessagesModule,
NotificationsModule,
SearchModule,
ShortcutsModule,
UserMenuModule,
UserModule,
SharedModule
],
exports : [

View File

@@ -3,7 +3,7 @@
class="dark bg-indigo-800 text-white print:hidden"
[mode]="isScreenSmall ? 'over' : 'side'"
[name]="'mainNavigation'"
[navigation]="data.navigation.futuristic"
[navigation]="navigation.futuristic"
[opened]="!isScreenSmall">
<!-- Navigation header hook -->
<ng-container fuseVerticalNavigationHeader>
@@ -18,10 +18,10 @@
<ng-container fuseVerticalNavigationFooter>
<!-- User -->
<div class="flex items-center w-full px-6 py-8 border-t">
<user-menu></user-menu>
<user></user>
<div class="flex flex-col w-full ml-4 overflow-hidden">
<div class="w-full whitespace-nowrap overflow-ellipsis overflow-hidden leading-normal text-current opacity-80">
{{data.user.name}}
{{user.name}}
</div>
<div class="w-full mt-0.5 whitespace-nowrap text-sm overflow-ellipsis overflow-hidden leading-normal text-current opacity-50">
brian.hughes@company.com
@@ -44,13 +44,13 @@
<mat-icon [svgIcon]="'heroicons_outline:menu'"></mat-icon>
</button>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<language></language>
<fuse-fullscreen></fuse-fullscreen>
<div class="flex items-center pl-2 ml-auto space-x-1 sm:space-x-2">
<languages></languages>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts [shortcuts]="data.shortcuts"></shortcuts>
<messages [messages]="data.messages"></messages>
<notifications [notifications]="data.notifications"></notifications>
<shortcuts></shortcuts>
<messages></messages>
<notifications></notifications>
</div>
</div>

View File

@@ -1,10 +1,13 @@
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { ActivatedRoute, Data, Router } from '@angular/router';
import { ActivatedRoute, Router } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation';
import { InitialData } from 'app/app.types';
import { Navigation } from 'app/core/navigation/navigation.types';
import { NavigationService } from 'app/core/navigation/navigation.service';
import { User } from 'app/core/user/user.types';
import { UserService } from 'app/core/user/user.service';
@Component({
selector : 'futuristic-layout',
@@ -13,8 +16,9 @@ import { InitialData } from 'app/app.types';
})
export class FuturisticLayoutComponent implements OnInit, OnDestroy
{
data: InitialData;
isScreenSmall: boolean;
navigation: Navigation;
user: User;
private _unsubscribeAll: Subject<any> = new Subject<any>();
/**
@@ -23,6 +27,8 @@ export class FuturisticLayoutComponent implements OnInit, OnDestroy
constructor(
private _activatedRoute: ActivatedRoute,
private _router: Router,
private _navigationService: NavigationService,
private _userService: UserService,
private _fuseMediaWatcherService: FuseMediaWatcherService,
private _fuseNavigationService: FuseNavigationService
)
@@ -50,10 +56,19 @@ export class FuturisticLayoutComponent implements OnInit, OnDestroy
*/
ngOnInit(): void
{
// Subscribe to the resolved route data
this._activatedRoute.data.subscribe((data: Data) => {
this.data = data.initialData;
});
// Subscribe to navigation data
this._navigationService.navigation$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((navigation: Navigation) => {
this.navigation = navigation;
});
// Subscribe to the user service
this._userService.user$
.pipe((takeUntil(this._unsubscribeAll)))
.subscribe((user: User) => {
this.user = user;
});
// Subscribe to media changes
this._fuseMediaWatcherService.onMediaChange$

View File

@@ -7,12 +7,12 @@ import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { FuseFullscreenModule } from '@fuse/components/fullscreen';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { LanguageModule } from 'app/layout/common/language/language.module';
import { LanguagesModule } from 'app/layout/common/languages/languages.module';
import { MessagesModule } from 'app/layout/common/messages/messages.module';
import { NotificationsModule } from 'app/layout/common/notifications/notifications.module';
import { SearchModule } from 'app/layout/common/search/search.module';
import { ShortcutsModule } from 'app/layout/common/shortcuts/shortcuts.module';
import { UserMenuModule } from 'app/layout/common/user-menu/user-menu.module';
import { UserModule } from 'app/layout/common/user/user.module';
import { SharedModule } from 'app/shared/shared.module';
import { FuturisticLayoutComponent } from 'app/layout/layouts/vertical/futuristic/futuristic.component';
@@ -29,12 +29,12 @@ import { FuturisticLayoutComponent } from 'app/layout/layouts/vertical/futuristi
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
LanguageModule,
LanguagesModule,
MessagesModule,
NotificationsModule,
SearchModule,
ShortcutsModule,
UserMenuModule,
UserModule,
SharedModule
],
exports : [

View File

@@ -4,7 +4,7 @@
[appearance]="'thin'"
[mode]="isScreenSmall ? 'over' : 'side'"
[name]="'mainNavigation'"
[navigation]="data.navigation.compact"
[navigation]="navigation.compact"
[opened]="!isScreenSmall">
<!-- Navigation header hook -->
<ng-container fuseVerticalNavigationContentHeader>
@@ -31,14 +31,14 @@
<mat-icon [svgIcon]="'heroicons_outline:menu'"></mat-icon>
</button>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<language></language>
<fuse-fullscreen></fuse-fullscreen>
<div class="flex items-center pl-2 ml-auto space-x-1 sm:space-x-2">
<languages></languages>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts [shortcuts]="data.shortcuts"></shortcuts>
<messages [messages]="data.messages"></messages>
<notifications [notifications]="data.notifications"></notifications>
<user-menu></user-menu>
<shortcuts></shortcuts>
<messages></messages>
<notifications></notifications>
<user></user>
</div>
</div>

View File

@@ -1,10 +1,11 @@
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { ActivatedRoute, Data, Router } from '@angular/router';
import { ActivatedRoute, Router } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation';
import { InitialData } from 'app/app.types';
import { Navigation } from 'app/core/navigation/navigation.types';
import { NavigationService } from 'app/core/navigation/navigation.service';
@Component({
selector : 'thin-layout',
@@ -13,8 +14,8 @@ import { InitialData } from 'app/app.types';
})
export class ThinLayoutComponent implements OnInit, OnDestroy
{
data: InitialData;
isScreenSmall: boolean;
navigation: Navigation;
private _unsubscribeAll: Subject<any> = new Subject<any>();
/**
@@ -23,6 +24,7 @@ export class ThinLayoutComponent implements OnInit, OnDestroy
constructor(
private _activatedRoute: ActivatedRoute,
private _router: Router,
private _navigationService: NavigationService,
private _fuseMediaWatcherService: FuseMediaWatcherService,
private _fuseNavigationService: FuseNavigationService
)
@@ -50,10 +52,12 @@ export class ThinLayoutComponent implements OnInit, OnDestroy
*/
ngOnInit(): void
{
// Subscribe to the resolved route data
this._activatedRoute.data.subscribe((data: Data) => {
this.data = data.initialData;
});
// Subscribe to navigation data
this._navigationService.navigation$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((navigation: Navigation) => {
this.navigation = navigation;
});
// Subscribe to media changes
this._fuseMediaWatcherService.onMediaChange$

View File

@@ -7,12 +7,12 @@ import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { FuseFullscreenModule } from '@fuse/components/fullscreen';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { LanguageModule } from 'app/layout/common/language/language.module';
import { LanguagesModule } from 'app/layout/common/languages/languages.module';
import { MessagesModule } from 'app/layout/common/messages/messages.module';
import { NotificationsModule } from 'app/layout/common/notifications/notifications.module';
import { SearchModule } from 'app/layout/common/search/search.module';
import { ShortcutsModule } from 'app/layout/common/shortcuts/shortcuts.module';
import { UserMenuModule } from 'app/layout/common/user-menu/user-menu.module';
import { UserModule } from 'app/layout/common/user/user.module';
import { SharedModule } from 'app/shared/shared.module';
import { ThinLayoutComponent } from 'app/layout/layouts/vertical/thin/thin.component';
@@ -29,12 +29,12 @@ import { ThinLayoutComponent } from 'app/layout/layouts/vertical/thin/thin.compo
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
LanguageModule,
LanguagesModule,
MessagesModule,
NotificationsModule,
SearchModule,
ShortcutsModule,
UserMenuModule,
UserModule,
SharedModule
],
exports : [

View File

@@ -33,10 +33,18 @@ export class FileManagerMockApi
// -----------------------------------------------------------------------------------------------------
this._fuseMockApiService
.onGet('api/apps/file-manager')
.reply(() => {
.reply(({request}) => {
// Clone the items
const items = cloneDeep(this._items);
let items = cloneDeep(this._items);
// See if a folder id exist
const folderId = request.params.get('folderId') ?? null;
// Filter the items by folder id. If folder id is null,
// that means we want to root items which have folder id
// of null
items = items.filter(item => item.folderId === folderId);
// Separate the items by folders and files
const folders = items.filter(item => item.type === 'folder');
@@ -46,11 +54,38 @@ export class FileManagerMockApi
folders.sort((a, b) => a.name.localeCompare(b.name));
files.sort((a, b) => a.name.localeCompare(b.name));
// Figure out the path and attach it to the response
// Prepare the empty paths array
const pathItems = cloneDeep(this._items);
const path = [];
// Prepare the current folder
let currentFolder = null;
// Get the current folder and add it as the first entry
if ( folderId )
{
currentFolder = pathItems.find(item => item.id === folderId);
path.push(currentFolder);
}
// Start traversing and storing the folders as a path array
// until we hit null on the folder id
while ( currentFolder?.folderId )
{
currentFolder = pathItems.find(item => item.id === currentFolder.folderId);
if ( currentFolder )
{
path.unshift(currentFolder);
}
}
return [
200,
{
folders,
files
files,
path
}
];
});

View File

@@ -2,6 +2,7 @@
export const items = [
{
id : 'cd6897cb-acfd-4016-8b53-3f66a5b5fc68',
folderId : null,
name : 'Personal',
createdBy : 'Brian Hughes',
createdAt : 'April 24, 2018',
@@ -13,6 +14,7 @@ export const items = [
},
{
id : '6da8747f-b474-4c9a-9eba-5ef212285500',
folderId : null,
name : 'Photos',
createdBy : 'Brian Hughes',
createdAt : 'November 01, 2021',
@@ -24,6 +26,7 @@ export const items = [
},
{
id : 'ed58add1-45a7-41db-887d-3ca7ee7f2719',
folderId : null,
name : 'Work',
createdBy : 'Brian Hughes',
createdAt : 'May 8, 2020',
@@ -35,6 +38,7 @@ export const items = [
},
{
id : '5cb66e32-d1ac-4b9a-8c34-5991ce25add2',
folderId : null,
name : 'Contract #123',
createdBy : 'Brian Hughes',
createdAt : 'January 14, 2021',
@@ -46,6 +50,7 @@ export const items = [
},
{
id : '3ffc3d84-8f2d-4929-903a-ef6fc21657a7',
folderId : null,
name : 'Estimated budget',
createdBy : 'Brian Hughes',
createdAt : 'December 14, 2020',
@@ -57,6 +62,7 @@ export const items = [
},
{
id : '157adb9a-14f8-4559-ac93-8be893c9f80a',
folderId : null,
name : 'DMCA notice #42',
createdBy : 'Brian Hughes',
createdAt : 'May 8, 2021',
@@ -68,6 +74,7 @@ export const items = [
},
{
id : '4f64597a-df7e-461c-ad60-f33e5f7e0747',
folderId : null,
name : 'Invoices',
createdBy : 'Brian Hughes',
createdAt : 'January 12, 2020',
@@ -79,6 +86,7 @@ export const items = [
},
{
id : 'e445c445-57b2-4476-8c62-b068e3774b8e',
folderId : null,
name : 'Crash logs',
createdBy : 'Brian Hughes',
createdAt : 'June 8, 2020',
@@ -90,6 +98,7 @@ export const items = [
},
{
id : 'b482f93e-7847-4614-ad48-b78b78309f81',
folderId : null,
name : 'System logs',
createdBy : 'Brian Hughes',
createdAt : 'June 8, 2020',
@@ -101,6 +110,7 @@ export const items = [
},
{
id : 'ec07a98d-2e5b-422c-a9b2-b5d1c0e263f5',
folderId : null,
name : 'Personal projects',
createdBy : 'Brian Hughes',
createdAt : 'March 18, 2020',
@@ -112,6 +122,7 @@ export const items = [
},
{
id : 'ae908d59-07da-4dd8-aba0-124e50289295',
folderId : null,
name : 'Biometric portrait',
createdBy : 'Brian Hughes',
createdAt : 'August 29, 2020',
@@ -123,6 +134,7 @@ export const items = [
},
{
id : '4038a5b6-5b1a-432d-907c-e037aeb817a8',
folderId : null,
name : 'Scanned image 20201012-1',
createdBy : 'Brian Hughes',
createdAt : 'September 13, 2020',
@@ -134,6 +146,7 @@ export const items = [
},
{
id : '630d2e9a-d110-47a0-ac03-256073a0f56d',
folderId : null,
name : 'Scanned image 20201012-2',
createdBy : 'Brian Hughes',
createdAt : 'September 14, 2020',
@@ -145,6 +158,7 @@ export const items = [
},
{
id : '1417d5ed-b616-4cff-bfab-286677b69d79',
folderId : null,
name : 'Prices',
createdBy : 'Brian Hughes',
createdAt : 'April 07, 2020',
@@ -156,6 +170,7 @@ export const items = [
},
{
id : 'bd2817c7-6751-40dc-b252-b6b5634c0689',
folderId : null,
name : 'Shopping list',
createdBy : 'Brian Hughes',
createdAt : 'March 26, 2021',
@@ -167,6 +182,7 @@ export const items = [
},
{
id : '14fb47c9-6eeb-4070-919c-07c8133285d1',
folderId : null,
name : 'Summer budget',
createdBy : 'Brian Hughes',
createdAt : 'June 02, 2020',
@@ -175,5 +191,67 @@ export const items = [
type : 'XLS',
contents : null,
description: null
},
{
id : '894e8514-03d3-4f5e-bb28-f6c092501fae',
folderId : 'cd6897cb-acfd-4016-8b53-3f66a5b5fc68',
name : 'A personal file',
createdBy : 'Brian Hughes',
createdAt : 'June 02, 2020',
modifiedAt : 'June 02, 2020',
size : '943 KB',
type : 'XLS',
contents : null,
description: null
},
{
id : '74010810-16cf-441d-a1aa-c9fb620fceea',
folderId : 'cd6897cb-acfd-4016-8b53-3f66a5b5fc68',
name : 'A personal folder',
createdBy : 'Brian Hughes',
createdAt : 'November 01, 2021',
modifiedAt : 'November 01, 2021',
size : '3015 MB',
type : 'folder',
contents : '907 files',
description: 'Personal photos; selfies, family, vacation and etc.'
},
{
id : 'a8c73e5a-8114-436d-ab54-d900b50b3762',
folderId : '74010810-16cf-441d-a1aa-c9fb620fceea',
name : 'A personal file within the personal folder',
createdBy : 'Brian Hughes',
createdAt : 'June 02, 2020',
modifiedAt : 'June 02, 2020',
size : '943 KB',
type : 'XLS',
contents : null,
description: null
},
{
id : '12d851a8-4f60-473e-8a59-abe4b422ea99',
folderId : '6da8747f-b474-4c9a-9eba-5ef212285500',
name : 'Photos file',
createdBy : 'Brian Hughes',
createdAt : 'June 02, 2020',
modifiedAt : 'June 02, 2020',
size : '943 KB',
type : 'XLS',
contents : null,
description: null
},
{
id : '2836766d-27e1-4f40-a31a-5a8419105e7e',
folderId : 'ed58add1-45a7-41db-887d-3ca7ee7f2719',
name : 'Work file',
createdBy : 'Brian Hughes',
createdAt : 'June 02, 2020',
modifiedAt : 'June 02, 2020',
size : '943 KB',
type : 'XLS',
contents : null,
description: null
}
];

View File

@@ -0,0 +1,454 @@
import { Injectable } from '@angular/core';
import { assign, cloneDeep } from 'lodash-es';
import { FuseMockApiService, FuseMockApiUtils } from '@fuse/lib/mock-api';
import { boards as boardsData, cards as cardsData, labels as labelsData, lists as listsData, members as membersData } from 'app/mock-api/apps/scrumboard/data';
@Injectable({
providedIn: 'root'
})
export class ScrumboardMockApi
{
// Private
private _boards: any[] = boardsData;
private _cards: any[] = cardsData;
private _labels: any[] = labelsData;
private _lists: any[] = listsData;
private _members: any[] = membersData;
/**
* Constructor
*/
constructor(private _fuseMockApiService: FuseMockApiService)
{
// Register Mock API handlers
this.registerHandlers();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Register Mock API handlers
*/
registerHandlers(): void
{
// -----------------------------------------------------------------------------------------------------
// @ Boards - GET
// -----------------------------------------------------------------------------------------------------
this._fuseMockApiService
.onGet('api/apps/scrumboard/boards')
.reply(({request}) => {
// Clone the boards
let boards = cloneDeep(this._boards);
// Go through the boards and inject the members
boards = boards.map(board => ({
...board,
members: board.members.map(boardMember => this._members.find(member => boardMember === member.id))
}));
return [
200,
boards
];
});
// -----------------------------------------------------------------------------------------------------
// @ Board - GET
// -----------------------------------------------------------------------------------------------------
this._fuseMockApiService
.onGet('api/apps/scrumboard/board')
.reply(({request}) => {
// Get the id
const id = request.params.get('id');
// Find the board
const board = this._boards.find(item => item.id === id);
// Attach the board lists
board.lists = this._lists.filter(item => item.boardId === id).sort((a, b) => a.position - b.position);
// Grab all cards that belong to this board and attach labels to them
let cards = this._cards.filter(item => item.boardId === id);
cards = cards.map(card => (
{
...card,
labels: card.labels.map(cardLabelId => this._labels.find(label => label.id === cardLabelId))
}
));
// Attach the board cards into corresponding lists
board.lists.forEach((list, index, array) => {
array[index].cards = cards.filter(item => item.boardId === id && item.listId === list.id).sort((a, b) => a.position - b.position);
});
// Attach the board labels
board.labels = this._labels.filter(item => item.boardId === id);
return [
200,
cloneDeep(board)
];
});
// -----------------------------------------------------------------------------------------------------
// @ List - POST
// -----------------------------------------------------------------------------------------------------
this._fuseMockApiService
.onPost('api/apps/scrumboard/board/list')
.reply(({request}) => {
// Get the list
const newList = cloneDeep(request.body.list);
// Generate a new GUID
newList.id = FuseMockApiUtils.guid();
// Store the new list
this._lists.push(newList);
return [
200,
newList
];
});
// -----------------------------------------------------------------------------------------------------
// @ List - PATCH
// -----------------------------------------------------------------------------------------------------
this._fuseMockApiService
.onPatch('api/apps/scrumboard/board/list')
.reply(({request}) => {
// Get the list
const list = cloneDeep(request.body.list);
// Prepare the updated list
let updatedList = null;
// Find the list and update it
this._lists.forEach((item, index, lists) => {
if ( item.id === list.id )
{
// Update the list
lists[index] = assign({}, lists[index], list);
// Store the updated list
updatedList = lists[index];
}
});
return [
200,
updatedList
];
});
// -----------------------------------------------------------------------------------------------------
// @ Lists - PATCH
// -----------------------------------------------------------------------------------------------------
this._fuseMockApiService
.onPatch('api/apps/scrumboard/board/lists')
.reply(({request}) => {
// Get the lists
const lists = cloneDeep(request.body.lists);
// Prepare the updated lists
const updatedLists = [];
// Go through the lists
lists.forEach((item) => {
// Find the list
const index = this._lists.findIndex(list => item.id === list.id);
// Update the list
this._lists[index] = assign({}, this._lists[index], item);
// Store in the updated lists
updatedLists.push(item);
});
return [
200,
updatedLists
];
});
// -----------------------------------------------------------------------------------------------------
// @ List - DELETE
// -----------------------------------------------------------------------------------------------------
this._fuseMockApiService
.onDelete('api/apps/scrumboard/board/list')
.reply(({request}) => {
// Get the id
const id = request.params.get('id');
// Find the list and delete it
const index = this._lists.findIndex(item => item.id === id);
this._lists.splice(index, 1);
// Filter out the cards that belonged to the list to delete them
this._cards = this._cards.filter(card => card.listId !== id);
return [
200,
true
];
});
// -----------------------------------------------------------------------------------------------------
// @ Card - PUT
// -----------------------------------------------------------------------------------------------------
this._fuseMockApiService
.onPut('api/apps/scrumboard/board/card')
.reply(({request}) => {
// Get the card
const newCard = cloneDeep(request.body.card);
// Generate a new GUID
newCard.id = FuseMockApiUtils.guid();
// Unshift the new card
this._cards.push(newCard);
return [
200,
newCard
];
});
// -----------------------------------------------------------------------------------------------------
// @ Card - PATCH
// -----------------------------------------------------------------------------------------------------
this._fuseMockApiService
.onPatch('api/apps/scrumboard/board/card')
.reply(({request}) => {
// Get the id and card
const id = request.body.id;
const card = cloneDeep(request.body.card);
// Prepare the updated card
let updatedCard = null;
// Go through the labels and leave only ids of them
card.labels = card.labels.map(itemLabel => itemLabel.id);
// Find the card and update it
this._cards.forEach((item, index, cards) => {
if ( item.id === id )
{
// Update the card
cards[index] = assign({}, cards[index], card);
// Store the updated card
updatedCard = cloneDeep(cards[index]);
}
});
// Attach the labels of the card
updatedCard.labels = updatedCard.labels.map(cardLabelId => this._labels.find(label => label.id === cardLabelId));
return [
200,
updatedCard
];
});
// -----------------------------------------------------------------------------------------------------
// @ Cards - PATCH
// -----------------------------------------------------------------------------------------------------
this._fuseMockApiService
.onPatch('api/apps/scrumboard/board/cards')
.reply(({request}) => {
// Get the cards
const cards = cloneDeep(request.body.cards);
// Prepare the updated cards
const updatedCards = [];
// Go through the cards
cards.forEach((item) => {
// Find the card
const index = this._cards.findIndex(card => item.id === card.id);
// Go through the labels and leave only ids of them
item.labels = item.labels.map(itemLabel => itemLabel.id);
// Update the card
this._cards[index] = assign({}, this._cards[index], item);
// Attach the labels of the card
item.labels = item.labels.map(cardLabelId => this._labels.find(label => label.id === cardLabelId));
// Store in the updated cards
updatedCards.push(item);
});
return [
200,
updatedCards
];
});
// -----------------------------------------------------------------------------------------------------
// @ Card - DELETE
// -----------------------------------------------------------------------------------------------------
this._fuseMockApiService
.onDelete('api/apps/scrumboard/board/card')
.reply(({request}) => {
// Get the id
const id = request.params.get('id');
// Find the card and delete it
const index = this._cards.findIndex(item => item.id === id);
this._cards.splice(index, 1);
return [
200,
true
];
});
// -----------------------------------------------------------------------------------------------------
// @ Card Positions - PATCH
// -----------------------------------------------------------------------------------------------------
this._fuseMockApiService
.onPatch('api/apps/scrumboard/board/card/positions')
.reply(({request}) => {
// Get the cards
const cards = request.body.cards;
// Go through the cards
this._cards.forEach((card) => {
// Find this card's index within the cards array that comes with the request
// and assign that index as the new position number for the card
card.position = cards.findIndex(item => item.id === card.id && item.listId === card.listId && item.boardId === card.boardId);
});
// Clone the cards
const updatedCards = cloneDeep(this._cards);
return [
200,
updatedCards
];
});
// -----------------------------------------------------------------------------------------------------
// @ Labels - GET
// -----------------------------------------------------------------------------------------------------
this._fuseMockApiService
.onGet('api/apps/scrumboard/board/labels')
.reply(({request}) => {
// Get the board id
const boardId = request.params.get('boardId');
// Filter the labels
const labels = this._labels.filter(item => item.boardId === boardId);
return [
200,
cloneDeep(labels)
];
});
// -----------------------------------------------------------------------------------------------------
// @ Label - PUT
// -----------------------------------------------------------------------------------------------------
this._fuseMockApiService
.onPut('api/apps/scrumboard/board/label')
.reply(({request}) => {
// Get the label
const newLabel = cloneDeep(request.body.label);
// Generate a new GUID
newLabel.id = FuseMockApiUtils.guid();
// Unshift the new label
this._labels.unshift(newLabel);
return [
200,
newLabel
];
});
// -----------------------------------------------------------------------------------------------------
// @ Label - PATCH
// -----------------------------------------------------------------------------------------------------
this._fuseMockApiService
.onPatch('api/apps/scrumboard/board/label')
.reply(({request}) => {
// Get the id and label
const id = request.body.id;
const label = cloneDeep(request.body.label);
// Prepare the updated label
let updatedLabel = null;
// Find the label and update it
this._labels.forEach((item, index, labels) => {
if ( item.id === id )
{
// Update the label
labels[index] = assign({}, labels[index], label);
// Store the updated label
updatedLabel = labels[index];
}
});
return [
200,
updatedLabel
];
});
// -----------------------------------------------------------------------------------------------------
// @ Label - DELETE
// -----------------------------------------------------------------------------------------------------
this._fuseMockApiService
.onDelete('api/apps/scrumboard/board/label')
.reply(({request}) => {
// Get the id
const id = request.params.get('id');
// Find the label and delete it
const index = this._labels.findIndex(item => item.id === id);
this._labels.splice(index, 1);
// Get the cards that have the label
const cardsWithLabel = this._cards.filter(card => card.labels.indexOf(id) > -1);
// Iterate through them and remove the label
cardsWithLabel.forEach((card) => {
card.tags.splice(card.tags.indexOf(id), 1);
});
return [
200,
true
];
});
}
}

View File

@@ -0,0 +1,334 @@
/* eslint-disable */
import * as moment from 'moment';
export const boards = [
{
id : '2c82225f-2a6c-45d3-b18a-1132712a4234',
title : 'Admin Dashboard',
description : 'Roadmap for the new project',
icon : 'heroicons_outline:template',
lastActivity: moment().startOf('day').subtract(1, 'day').toISOString(),
members : [
'9c510cf3-460d-4a8c-b3be-bcc3db578c08',
'baa88231-0ee6-4028-96d5-7f187e0f4cd5',
'18bb18f3-ea7d-4465-8913-e8c9adf6f568'
]
},
{
id : '0168b519-3dab-4b46-b2ea-0e678e38a583',
title : 'Weekly Planning',
description : 'Job related tasks for the week',
icon : 'heroicons_outline:calendar',
lastActivity: moment().startOf('day').subtract(2, 'days').toISOString(),
members : [
'79ebb9ee-1e57-4706-810c-03edaec8f56d',
'319ecb5b-f99c-4ee4-81b2-3aeffd1d4735',
'5bf7ed5b-8b04-46b7-b364-005958b7d82e',
'd1f612e6-3e3b-481f-a8a9-f917e243b06e',
'fe0fec0d-002b-406f-87ab-47eb87ba577c',
'23a47d2c-c6cb-40cc-af87-e946a9df5028',
'6726643d-e8dc-42fa-83a6-b4ec06921a6b',
'0d1eb062-13d5-4286-b8d4-e0bea15f3d56'
]
},
{
id : 'bc7db965-3c4f-4233-abf5-69bd70c3c175',
title : 'Personal Tasks',
description : 'Personal tasks around the house',
icon : 'heroicons_outline:home',
lastActivity: moment().startOf('day').subtract(1, 'week').toISOString(),
members : [
'6f6a1c34-390b-4b2e-97c8-ff0e0d787839'
]
}
];
export const lists = [
{
id : 'a2df7786-519c-485a-a85f-c09a61cc5f37',
boardId : '2c82225f-2a6c-45d3-b18a-1132712a4234',
position: 65536,
title : 'To do'
},
{
id : '83ca2a34-65af-49c0-a42e-94a34003fcf2',
boardId : '2c82225f-2a6c-45d3-b18a-1132712a4234',
position: 131072,
title : 'In progress'
},
{
id : 'a85ea483-f8f7-42d9-a314-3fed6aac22ab',
boardId : '2c82225f-2a6c-45d3-b18a-1132712a4234',
position: 196608,
title : 'In review'
},
{
id : '34cbef38-5687-4813-bd66-141a6df6d832',
boardId : '2c82225f-2a6c-45d3-b18a-1132712a4234',
position: 262144,
title : 'Completed'
}
];
export const cards = [
{
id : 'e74e66e9-fe0f-441e-a8ce-28ed6eccc48d',
boardId : '2c82225f-2a6c-45d3-b18a-1132712a4234',
listId : 'a2df7786-519c-485a-a85f-c09a61cc5f37',
position : 65536,
title : 'Example that showcase all of the available bits on the card with a fairly long title compared to other cards',
description: 'Example that showcase all of the available bits on the card with a fairly long title compared to other cards. Example that showcase all of the available bits on the card with a fairly long title compared to other cards.',
labels : [
'e0175175-2784-48f1-a519-a1d2e397c9b3',
'51779701-818a-4a53-bc16-137c3bd7a564',
'e8364d69-9595-46ce-a0f9-ce428632a0ac',
'caff9c9b-a198-4564-b1f4-8b3df1d345bb',
'f9eeb436-13a3-4208-a239-0d555960a567'
],
dueDate : moment().subtract(10, 'days').startOf('day').toISOString()
},
{
id : 'ed58add1-45a7-41db-887d-3ca7ee7f2719',
boardId : '2c82225f-2a6c-45d3-b18a-1132712a4234',
listId : 'a2df7786-519c-485a-a85f-c09a61cc5f37',
position: 131072,
title : 'Do a research about most needed admin applications',
labels : [
'e0175175-2784-48f1-a519-a1d2e397c9b3'
],
dueDate : null
},
{
id : 'cd6897cb-acfd-4016-8b53-3f66a5b5fc68',
boardId : '2c82225f-2a6c-45d3-b18a-1132712a4234',
listId : 'a2df7786-519c-485a-a85f-c09a61cc5f37',
position: 196608,
title : 'Implement the Project dashboard',
labels : [
'caff9c9b-a198-4564-b1f4-8b3df1d345bb'
],
dueDate : moment().startOf('day').toISOString()
},
{
id : '6da8747f-b474-4c9a-9eba-5ef212285500',
boardId : '2c82225f-2a6c-45d3-b18a-1132712a4234',
listId : 'a2df7786-519c-485a-a85f-c09a61cc5f37',
position: 262144,
title : 'Implement the Analytics dashboard',
labels : [
'caff9c9b-a198-4564-b1f4-8b3df1d345bb'
],
dueDate : moment().subtract(1, 'day').startOf('day').toISOString()
},
{
id : '94fb1dee-dd83-4cca-acdd-02e96d3cc4f1',
boardId : '2c82225f-2a6c-45d3-b18a-1132712a4234',
listId : '83ca2a34-65af-49c0-a42e-94a34003fcf2',
position: 65536,
title : 'Analytics dashboard design',
labels : [
'e8364d69-9595-46ce-a0f9-ce428632a0ac'
],
dueDate : null
},
{
id : 'fc16f7d8-957d-43ed-ba85-20f99b5ce011',
boardId : '2c82225f-2a6c-45d3-b18a-1132712a4234',
listId : '83ca2a34-65af-49c0-a42e-94a34003fcf2',
position: 131072,
title : 'Project dashboard design',
labels : [
'e8364d69-9595-46ce-a0f9-ce428632a0ac'
],
dueDate : null
},
{
id : 'c0b32f1f-64ec-4f8d-8b11-a8dc809df331',
boardId : '2c82225f-2a6c-45d3-b18a-1132712a4234',
listId : 'a85ea483-f8f7-42d9-a314-3fed6aac22ab',
position: 65536,
title : 'JWT Auth implementation',
labels : [
'caff9c9b-a198-4564-b1f4-8b3df1d345bb'
],
dueDate : null
},
{
id : '532c2747-be79-464a-9897-6a682bf22b64',
boardId : '2c82225f-2a6c-45d3-b18a-1132712a4234',
listId : '34cbef38-5687-4813-bd66-141a6df6d832',
position: 65536,
title : 'Create low fidelity wireframes',
labels : [],
dueDate : null
},
{
id : '1d908efe-c830-476e-9e87-d06e30d89bc2',
boardId : '2c82225f-2a6c-45d3-b18a-1132712a4234',
listId : '34cbef38-5687-4813-bd66-141a6df6d832',
position: 131072,
title : 'Create high fidelity wireframes',
labels : [],
dueDate : moment().subtract(10, 'day').startOf('day').toISOString()
},
{
id : 'b1da11ed-7896-4826-962d-4b7b718896d4',
boardId : '2c82225f-2a6c-45d3-b18a-1132712a4234',
listId : '34cbef38-5687-4813-bd66-141a6df6d832',
position: 196608,
title : 'Collect information about most used admin layouts',
labels : [
'e0175175-2784-48f1-a519-a1d2e397c9b3'
],
dueDate : null
},
{
id : '3b7f3ceb-107f-42bc-a204-c268c9a56cb4',
boardId : '2c82225f-2a6c-45d3-b18a-1132712a4234',
listId : '34cbef38-5687-4813-bd66-141a6df6d832',
position: 262144,
title : 'Do a research about latest UI trends',
labels : [
'e0175175-2784-48f1-a519-a1d2e397c9b3'
],
dueDate : null
},
{
id : 'cd7f01c5-a941-4076-8cef-37da0354e643',
boardId : '2c82225f-2a6c-45d3-b18a-1132712a4234',
listId : '34cbef38-5687-4813-bd66-141a6df6d832',
position: 327680,
title : 'Learn more about UX',
labels : [
'e0175175-2784-48f1-a519-a1d2e397c9b3'
],
dueDate : null
}
];
export const labels = [
{
id : 'e0175175-2784-48f1-a519-a1d2e397c9b3',
boardId: '2c82225f-2a6c-45d3-b18a-1132712a4234',
title : 'Research'
},
{
id : '51779701-818a-4a53-bc16-137c3bd7a564',
boardId: '2c82225f-2a6c-45d3-b18a-1132712a4234',
title : 'Wireframing'
},
{
id : 'e8364d69-9595-46ce-a0f9-ce428632a0ac',
boardId: '2c82225f-2a6c-45d3-b18a-1132712a4234',
title : 'Design'
},
{
id : 'caff9c9b-a198-4564-b1f4-8b3df1d345bb',
boardId: '2c82225f-2a6c-45d3-b18a-1132712a4234',
title : 'Development'
},
{
id : 'f9eeb436-13a3-4208-a239-0d555960a567',
boardId: '2c82225f-2a6c-45d3-b18a-1132712a4234',
title : 'Bug'
}
];
export const members = [
{
id : '6f6a1c34-390b-4b2e-97c8-ff0e0d787839',
name : 'Angeline Vinson',
avatar: 'assets/images/avatars/female-01.jpg'
},
{
id : '4ce4be48-c8c0-468d-9df8-ddfda14cdb37',
name : 'Roseann Greer',
avatar: 'assets/images/avatars/female-02.jpg'
},
{
id : '9c510cf3-460d-4a8c-b3be-bcc3db578c08',
name : 'Lorraine Barnett',
avatar: 'assets/images/avatars/female-03.jpg'
},
{
id : '7ec887d9-b01a-4057-b5dc-aaed18637cc1',
name : 'Middleton Bradford',
avatar: 'assets/images/avatars/male-01.jpg'
},
{
id : '74975a82-addb-427b-9b43-4d2e03331b68',
name : 'Sue Hays',
avatar: 'assets/images/avatars/female-04.jpg'
},
{
id : '18bb18f3-ea7d-4465-8913-e8c9adf6f568',
name : 'Keith Neal',
avatar: 'assets/images/avatars/male-02.jpg'
},
{
id : 'baa88231-0ee6-4028-96d5-7f187e0f4cd5',
name : 'Wilkins Gilmore',
avatar: 'assets/images/avatars/male-03.jpg'
},
{
id : '0d1eb062-13d5-4286-b8d4-e0bea15f3d56',
name : 'Baldwin Stein',
avatar: 'assets/images/avatars/male-04.jpg'
},
{
id : '5bf7ed5b-8b04-46b7-b364-005958b7d82e',
name : 'Bobbie Cohen',
avatar: 'assets/images/avatars/female-05.jpg'
},
{
id : '93b1a72b-e2db-4f77-82d6-272047433508',
name : 'Melody Peters',
avatar: 'assets/images/avatars/female-06.jpg'
},
{
id : 'd1f612e6-3e3b-481f-a8a9-f917e243b06e',
name : 'Marquez Ryan',
avatar: 'assets/images/avatars/male-05.jpg'
},
{
id : '79ebb9ee-1e57-4706-810c-03edaec8f56d',
name : 'Roberta Briggs',
avatar: 'assets/images/avatars/female-07.jpg'
},
{
id : '6726643d-e8dc-42fa-83a6-b4ec06921a6b',
name : 'Robbie Buckley',
avatar: 'assets/images/avatars/female-08.jpg'
},
{
id : '8af617d7-898e-4992-beda-d5ac1d7ceda4',
name : 'Garcia Whitney',
avatar: 'assets/images/avatars/male-06.jpg'
},
{
id : 'bcff44c4-9943-4adc-9049-08b1d922a658',
name : 'Spencer Pate',
avatar: 'assets/images/avatars/male-07.jpg'
},
{
id : '54160ca2-29c9-4475-88a1-31a9307ad913',
name : 'Monica Mcdaniel',
avatar: 'assets/images/avatars/female-09.jpg'
},
{
id : '51286603-3a43-444e-9242-f51fe57d5363',
name : 'Mcmillan Durham',
avatar: 'assets/images/avatars/male-08.jpg'
},
{
id : '319ecb5b-f99c-4ee4-81b2-3aeffd1d4735',
name : 'Jeoine Hebert',
avatar: 'assets/images/avatars/female-10.jpg'
},
{
id : 'fe0fec0d-002b-406f-87ab-47eb87ba577c',
name : 'Susanna Kline',
avatar: 'assets/images/avatars/female-11.jpg'
},
{
id : '23a47d2c-c6cb-40cc-af87-e946a9df5028',
name : 'Suzette Singleton',
avatar: 'assets/images/avatars/female-12.jpg'
}
];

View File

@@ -134,6 +134,13 @@ export const defaultNavigation: FuseNavigationItem[] = [
icon : 'heroicons_outline:pencil-alt',
link : '/apps/notes'
},
{
id : 'apps.scrumboard',
title: 'Scrumboard',
type : 'basic',
icon : 'heroicons_outline:view-boards',
link : '/apps/scrumboard'
},
{
id : 'apps.tasks',
title: 'Tasks',
@@ -141,7 +148,6 @@ export const defaultNavigation: FuseNavigationItem[] = [
icon : 'heroicons_outline:check-circle',
link : '/apps/tasks'
}
]
},
{
@@ -590,7 +596,7 @@ export const defaultNavigation: FuseNavigationItem[] = [
id : 'pages.invoice',
title : 'Invoice',
type : 'collapsable',
icon : 'heroicons_outline:exclamation-circle',
icon : 'heroicons_outline:calculator',
children: [
{
id : 'pages.invoice.printable',
@@ -676,10 +682,10 @@ export const defaultNavigation: FuseNavigationItem[] = [
icon : 'heroicons_outline:collection',
children: [
{
id : 'user-interface.angular-material',
title: 'Angular Material',
id : 'user-interface.material-components',
title: 'Material components',
type : 'basic',
icon : 'heroicons_outline:shield-check',
icon : 'heroicons_outline:chip',
link : '/ui/angular-material'
},
{
@@ -689,6 +695,13 @@ export const defaultNavigation: FuseNavigationItem[] = [
icon : 'heroicons_outline:sparkles',
link : '/ui/tailwindcss'
},
{
id : 'user-interface.advanced-search',
title: 'Advanced search',
type : 'basic',
icon : 'heroicons_outline:search-circle',
link : '/ui/advanced-search'
},
{
id : 'user-interface.animations',
title: 'Animations',
@@ -855,10 +868,16 @@ export const defaultNavigation: FuseNavigationItem[] = [
type : 'collapsable',
children: [
{
id : 'user-interface.page-layouts.simple.fullwidth',
title: 'Fullwidth',
id : 'user-interface.page-layouts.simple.fullwidth-1',
title: 'Fullwidth #1',
type : 'basic',
link : '/ui/page-layouts/simple/fullwidth'
link : '/ui/page-layouts/simple/fullwidth-1'
},
{
id : 'user-interface.page-layouts.simple.fullwidth-2',
title: 'Fullwidth #2',
type : 'basic',
link : '/ui/page-layouts/simple/fullwidth-2'
},
{
id : 'user-interface.page-layouts.simple.left-sidebar-1',
@@ -927,7 +946,7 @@ export const defaultNavigation: FuseNavigationItem[] = [
icon : 'heroicons_outline:speakerphone',
link : '/docs/changelog',
badge: {
title : '13.0.2',
title : '13.2.0',
classes: 'px-2 bg-yellow-300 text-black rounded-full'
}
},
@@ -1116,6 +1135,7 @@ export const compactNavigation: FuseNavigationItem[] = [
{
id : 'dashboards',
title : 'Dashboards',
tooltip : 'Dashboards',
type : 'aside',
icon : 'heroicons_outline:home',
children: [] // This will be filled from defaultNavigation so we don't have to manage multiple sets of the same navigation
@@ -1123,6 +1143,7 @@ export const compactNavigation: FuseNavigationItem[] = [
{
id : 'apps',
title : 'Apps',
tooltip : 'Apps',
type : 'aside',
icon : 'heroicons_outline:qrcode',
children: [] // This will be filled from defaultNavigation so we don't have to manage multiple sets of the same navigation
@@ -1130,6 +1151,7 @@ export const compactNavigation: FuseNavigationItem[] = [
{
id : 'pages',
title : 'Pages',
tooltip : 'Pages',
type : 'aside',
icon : 'heroicons_outline:document-duplicate',
children: [] // This will be filled from defaultNavigation so we don't have to manage multiple sets of the same navigation
@@ -1137,6 +1159,7 @@ export const compactNavigation: FuseNavigationItem[] = [
{
id : 'user-interface',
title : 'UI',
tooltip : 'UI',
type : 'aside',
icon : 'heroicons_outline:collection',
children: [] // This will be filled from defaultNavigation so we don't have to manage multiple sets of the same navigation
@@ -1144,140 +1167,24 @@ export const compactNavigation: FuseNavigationItem[] = [
{
id : 'navigation-features',
title : 'Navigation',
tooltip : 'Navigation',
type : 'aside',
icon : 'heroicons_outline:menu',
children: [] // This will be filled from defaultNavigation so we don't have to manage multiple sets of the same navigation
}
];
export const futuristicNavigation: FuseNavigationItem[] = [
{
id : 'dashboards',
title : 'DASHBOARDS',
type : 'group',
children: [] // This will be filled from defaultNavigation so we don't have to manage multiple sets of the same navigation
},
{
id : 'apps',
title : 'APPS',
type : 'group',
children: [
{
id : 'apps.dashboards.analytics',
title: 'Analytics',
type : 'basic',
icon : 'heroicons_outline:chart-pie',
link : '/dashboards/analytics'
},
{
id : 'apps.dashboards.project',
title: 'Project',
type : 'basic',
icon : 'heroicons_outline:clipboard-check',
link : '/dashboards/project'
},
{
id : 'apps.academy',
title: 'Academy',
type : 'basic',
icon : 'heroicons_outline:academic-cap',
link : '/apps/academy'
},
{
id : 'apps.calendar',
title: 'Calendar',
type : 'basic',
icon : 'heroicons_outline:calendar',
link : '/apps/calendar'
},
{
id : 'apps.chat',
title: 'Chat',
type : 'basic',
icon : 'heroicons_outline:chat-alt',
link : '/apps/chat'
},
{
id : 'apps.contacts',
title: 'Contacts',
type : 'basic',
icon : 'heroicons_outline:user-group',
link : '/apps/contacts'
},
{
id : 'apps.ecommerce',
title : 'ECommerce',
type : 'collapsable',
icon : 'heroicons_outline:shopping-cart',
children: [
{
id : 'apps.ecommerce.inventory',
title: 'Inventory',
type : 'basic',
link : '/apps/ecommerce/inventory'
}
]
},
{
id : 'apps.file-manager',
title: 'File manager',
type : 'basic',
icon : 'heroicons_outline:shopping-cart',
link : '/apps/file-manager'
},
{
id : 'apps.help-center',
title : 'Help center',
type : 'collapsable',
icon : 'heroicons_outline:support',
link : '/apps/help-center',
children: [
{
id : 'apps.help-center.home',
title : 'Home',
type : 'basic',
link : '/apps/help-center',
exactMatch: true
},
{
id : 'apps.help-center.faqs',
title: 'FAQs',
type : 'basic',
link : '/apps/help-center/faqs'
},
{
id : 'apps.help-center.guides',
title: 'Guides',
type : 'basic',
link : '/apps/help-center/guides'
},
{
id : 'apps.help-center.support',
title: 'Support',
type : 'basic',
link : '/apps/help-center/support'
}
]
},
{
id : 'apps.mailbox',
title: 'Mailbox',
type : 'basic',
icon : 'heroicons_outline:mail',
link : '/apps/mailbox',
badge: {
title : '27',
classes: 'px-2 bg-black bg-opacity-25 text-white rounded-full'
}
},
{
id : 'apps.notes',
title: 'Notes',
type : 'basic',
icon : 'heroicons_outline:pencil-alt',
link : '/apps/notes'
},
{
id : 'apps.tasks',
title: 'Tasks',
type : 'basic',
icon : 'heroicons_outline:check-circle',
link : '/apps/tasks'
}
]
children: [] // This will be filled from defaultNavigation so we don't have to manage multiple sets of the same navigation
},
{
id : 'others',

View File

@@ -4,7 +4,7 @@ import * as moment from 'moment';
export const notifications = [
{
id : '493190c9-5b61-4912-afe5-78c21f1044d7',
icon : 'heroicons_outline:star',
icon : 'heroicons_solid:star',
title : 'Daily challenges',
description: 'Your submission has been accepted',
time : moment().subtract(25, 'minutes').toISOString(), // 25 minutes ago
@@ -21,7 +21,7 @@ export const notifications = [
},
{
id : 'b91ccb58-b06c-413b-b389-87010e03a120',
icon : 'heroicons_outline:mail',
icon : 'heroicons_solid:mail',
title : 'Mailbox',
description: 'You have 15 unread mails across 3 mailboxes',
time : moment().subtract(3, 'hours').toISOString(), // 3 hours ago
@@ -31,7 +31,7 @@ export const notifications = [
},
{
id : '541416c9-84a7-408a-8d74-27a43c38d797',
icon : 'heroicons_outline:refresh',
icon : 'heroicons_solid:refresh',
title : 'Cron jobs',
description: 'Your <em>Docker container</em> is ready to publish',
time : moment().subtract(5, 'hours').toISOString(), // 5 hours ago
@@ -59,7 +59,7 @@ export const notifications = [
},
{
id : 'b85c2338-cc98-4140-bbf8-c226ce4e395e',
icon : 'heroicons_outline:mail',
icon : 'heroicons_solid:mail',
title : 'Mailbox',
description: 'You have 3 new mails',
time : moment().subtract(1, 'day').toISOString(), // 1 day ago
@@ -69,7 +69,7 @@ export const notifications = [
},
{
id : '8f8e1bf9-4661-4939-9e43-390957b60f42',
icon : 'heroicons_outline:star',
icon : 'heroicons_solid:star',
title : 'Daily challenges',
description: 'Your submission has been accepted and you are ready to sign-up for the final assigment which will be ready in 2 days',
time : moment().subtract(3, 'days').toISOString(), // 3 days ago
@@ -79,7 +79,7 @@ export const notifications = [
},
{
id : '30af917b-7a6a-45d1-822f-9e7ad7f8bf69',
icon : 'heroicons_outline:refresh',
icon : 'heroicons_solid:refresh',
title : 'Cron jobs',
description: 'Your Vagrant container is ready to download',
time : moment().subtract(4, 'day').toISOString(), // 4 days ago

View File

@@ -4,6 +4,7 @@ import { FuseNavigationItem, FuseNavigationService } from '@fuse/components/navi
import { FuseMockApiService } from '@fuse/lib/mock-api';
import { defaultNavigation } from 'app/mock-api/common/navigation/data';
import { contacts } from 'app/mock-api/apps/contacts/data';
import { tasks } from 'app/mock-api/apps/tasks/data';
@Injectable({
providedIn: 'root'
@@ -12,6 +13,7 @@ export class SearchMockApi
{
private readonly _defaultNavigation: FuseNavigationItem[] = defaultNavigation;
private readonly _contacts: any[] = contacts;
private readonly _tasks: any[] = tasks;
/**
* Constructor
@@ -54,58 +56,75 @@ export class SearchMockApi
return [200, {results: []}];
}
// Filter the navigation
const navigationResults = cloneDeep(flatNavigation).filter(item => (item.title?.toLowerCase().includes(query) || (item.subtitle && item.subtitle.includes(query))));
// Filter the contacts
const contactsResults = cloneDeep(this._contacts).filter(user => user.name.toLowerCase().includes(query));
const contactsResults = cloneDeep(this._contacts)
.filter(contact => contact.name.toLowerCase().includes(query));
// Create the results array
// Filter the navigation
const pagesResults = cloneDeep(flatNavigation)
.filter(page => (page.title?.toLowerCase().includes(query) || (page.subtitle && page.subtitle.includes(query))));
// Filter the tasks
const tasksResults = cloneDeep(this._tasks)
.filter(task => task.title.toLowerCase().includes(query));
// Prepare the results array
const results = [];
// If there are navigation results...
if ( navigationResults.length > 0 )
{
// Normalize the results while marking the found chars
navigationResults.forEach((result: any) => {
// Normalize
result['hint'] = result.link;
result['resultType'] = 'page';
// Mark the found chars
const re = new RegExp('(' + query.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + ')', 'ig');
result.title = result.title.replace(re, '<mark>$1</mark>');
});
// Add the results
results.push(...navigationResults);
}
// If there are contacts results...
if ( contactsResults.length > 0 )
{
// Normalize the results while marking the found chars
// Normalize the results
contactsResults.forEach((result) => {
// Normalize
result.title = result.name;
result.resultType = 'contact';
// Make the found chars bold
const re = new RegExp('(' + query.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + ')', 'ig');
result.title = result.title.replace(re, '<mark>$1</mark>');
// Add a link
result.link = '/apps/contacts/' + result.id;
});
// Add the results to the results object
results.push(...contactsResults);
// Add to the results
results.push({
id : 'contacts',
label : 'Contacts',
results: contactsResults
});
}
// If there are page results...
if ( pagesResults.length > 0 )
{
// Normalize the results
pagesResults.forEach((result: any) => {
});
// Add to the results
results.push({
id : 'pages',
label : 'Pages',
results: pagesResults
});
}
// If there are tasks results...
if ( tasksResults.length > 0 )
{
// Normalize the results
tasksResults.forEach((result) => {
// Add a link
result.link = '/apps/tasks/' + result.id;
});
// Add to the results
results.push({
id : 'tasks',
label : 'Tasks',
results: tasksResults
});
}
// Return the response
return [200, {results}];
return [200, results];
});
}
}

View File

@@ -3,9 +3,9 @@ export const shortcuts = [
{
id : 'a1ae91d3-e2cb-459b-9be9-a184694f548b',
label : 'Changelog',
description: 'Latest version: v1.2',
description: 'List of changes',
icon : 'heroicons_outline:clipboard-list',
link : '/dashboards/project',
link : '/docs/changelog',
useRouter : true
},
{
@@ -13,7 +13,7 @@ export const shortcuts = [
label : 'Documentation',
description: 'Getting started',
icon : 'heroicons_outline:book-open',
link : '/dashboards/project',
link : '/docs/guides/getting-started/introduction',
useRouter : true
},
{
@@ -21,7 +21,7 @@ export const shortcuts = [
label : 'Help center',
description: 'FAQs and guides',
icon : 'heroicons_outline:support',
link : '/pages/help-center',
link : '/apps/help-center',
useRouter : true
},
{
@@ -29,7 +29,7 @@ export const shortcuts = [
label : 'Dashboard',
description: 'User analytics',
icon : 'heroicons_outline:chart-pie',
link : '/dashboards/project',
link : '/dashboards/analytics',
useRouter : true
},
{
@@ -67,7 +67,7 @@ export const shortcuts = [
{
id : '0a240ab8-e19d-4503-bf68-20013030d526',
label : 'Reload',
description: 'Restart the app',
description: 'Reload the app',
icon : 'heroicons_outline:refresh',
link : '/dashboards/project',
useRouter : false

View File

@@ -16,6 +16,7 @@ import { NotesMockApi } from 'app/mock-api/apps/notes/api';
import { NotificationsMockApi } from 'app/mock-api/common/notifications/api';
import { ProjectMockApi } from 'app/mock-api/dashboards/project/api';
import { SearchMockApi } from 'app/mock-api/common/search/api';
import { ScrumboardMockApi } from 'app/mock-api/apps/scrumboard/api';
import { ShortcutsMockApi } from 'app/mock-api/common/shortcuts/api';
import { TasksMockApi } from 'app/mock-api/apps/tasks/api';
import { UserMockApi } from 'app/mock-api/common/user/api';
@@ -39,6 +40,7 @@ export const mockApiServices = [
NotificationsMockApi,
ProjectMockApi,
SearchMockApi,
ScrumboardMockApi,
ShortcutsMockApi,
TasksMockApi,
UserMockApi

View File

@@ -341,8 +341,8 @@
<mat-form-field class="fuse-mat-textarea fuse-mat-no-subscript flex-auto">
<textarea
matInput
cdkTextareaAutosize
[cdkAutosizeMinRows]="1"
matTextareaAutosize
[matAutosizeMinRows]="1"
[formControlName]="'description'"
[placeholder]="'Event description'">
</textarea>

View File

@@ -7,7 +7,6 @@ import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatMenuModule } from '@angular/material/menu';
import { MatSidenavModule } from '@angular/material/sidenav';
import { FuseAutogrowModule } from '@fuse/directives/autogrow';
import { SharedModule } from 'app/shared/shared.module';
import { chatRoutes } from 'app/modules/admin/apps/chat/chat.routing';
import { ChatComponent } from 'app/modules/admin/apps/chat/chat.component';
@@ -35,7 +34,6 @@ import { ProfileComponent } from 'app/modules/admin/apps/chat/profile/profile.co
MatInputModule,
MatMenuModule,
MatSidenavModule,
FuseAutogrowModule,
SharedModule
]
})

View File

@@ -17,7 +17,6 @@ import { MatSidenavModule } from '@angular/material/sidenav';
import { MatTableModule } from '@angular/material/table';
import { MatTooltipModule } from '@angular/material/tooltip';
import * as moment from 'moment';
import { FuseAutogrowModule } from '@fuse/directives/autogrow';
import { FuseFindByKeyPipeModule } from '@fuse/pipes/find-by-key';
import { SharedModule } from 'app/shared/shared.module';
import { contactsRoutes } from 'app/modules/admin/apps/contacts/contacts.routing';
@@ -49,7 +48,6 @@ import { ContactsListComponent } from 'app/modules/admin/apps/contacts/list/list
MatSidenavModule,
MatTableModule,
MatTooltipModule,
FuseAutogrowModule,
FuseFindByKeyPipeModule,
SharedModule
],

View File

@@ -598,11 +598,11 @@
[svgIcon]="'heroicons_solid:menu-alt-2'"></mat-icon>
<textarea
matInput
fuseAutogrow
[rows]="5"
[formControlName]="'notes'"
[placeholder]="'Notes'"
[spellcheck]="false"></textarea>
[rows]="5"
[spellcheck]="false"
matTextareaAutosize></textarea>
</mat-form-field>
</div>

View File

@@ -369,9 +369,9 @@ export class ContactsDetailsComponent implements OnInit, OnDestroy
scrollStrategy : this._overlay.scrollStrategies.block(),
positionStrategy: this._overlay.position()
.flexibleConnectedTo(this._tagsPanelOrigin.nativeElement)
.withFlexibleDimensions()
.withFlexibleDimensions(true)
.withViewportMargin(64)
.withLockedPosition()
.withLockedPosition(true)
.withPositions([
{
originX : 'start',

View File

@@ -12,7 +12,6 @@ import { MatRippleModule } from '@angular/material/core';
import { MatSortModule } from '@angular/material/sort';
import { MatSelectModule } from '@angular/material/select';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatTableModule } from '@angular/material/table';
import { MatTooltipModule } from '@angular/material/tooltip';
import { SharedModule } from 'app/shared/shared.module';
import { InventoryComponent } from 'app/modules/admin/apps/ecommerce/inventory/inventory.component';
@@ -38,7 +37,6 @@ import { ecommerceRoutes } from 'app/modules/admin/apps/ecommerce/ecommerce.rout
MatSortModule,
MatSelectModule,
MatSlideToggleModule,
MatTableModule,
MatTooltipModule,
SharedModule
]

View File

@@ -1,4 +1,4 @@
<div class="absolute inset-0 flex flex-col min-w-0 overflow-hidden bg-card dark:bg-transparent">
<div class="sm:absolute sm:inset-0 flex flex-col flex-auto min-w-0 sm:overflow-hidden bg-card dark:bg-transparent">
<!-- Header -->
<div class="relative flex flex-col sm:flex-row flex-0 sm:items-center sm:justify-between py-8 px-6 md:px-8 border-b">
@@ -39,531 +39,451 @@
<div class="flex flex-auto overflow-hidden">
<!-- Products list -->
<div class="flex flex-col flex-auto sm:mb-18 overflow-hidden">
<ng-container *ngIf="productsCount > 0; else noProducts">
<!-- Table wrapper -->
<div
class="overflow-x-auto sm:overflow-y-auto"
cdkScrollable>
<!-- Table -->
<table
class="w-full min-w-320 table-fixed bg-transparent"
[ngClass]="{'pointer-events-none': isLoading}"
mat-table
matSort
[matSortActive]="'name'"
[matSortDisableClear]="true"
[matSortDirection]="'asc'"
[multiTemplateDataRows]="true"
[dataSource]="products$"
[trackBy]="trackByFn">
<!-- SKU -->
<ng-container matColumnDef="sku">
<th
class="w-56 pl-26 bg-gray-50 dark:bg-black dark:bg-opacity-5"
mat-header-cell
*matHeaderCellDef
mat-sort-header
disableClear>
<div class="flex flex-col flex-auto sm:mb-18 overflow-hidden sm:overflow-y-auto">
<ng-container *ngIf="(products$ | async) as products">
<ng-container *ngIf="products.length > 0; else noProducts">
<div class="grid">
<!-- Header -->
<div
class="inventory-grid z-10 sticky top-0 grid gap-4 py-4 px-6 md:px-8 shadow text-md font-semibold text-secondary bg-gray-50 dark:bg-black dark:bg-opacity-5"
matSort
matSortDisableClear>
<div></div>
<div
class="hidden md:block"
[mat-sort-header]="'sku'">
SKU
</th>
<td
class="px-8"
mat-cell
*matCellDef="let product">
<div class="flex items-center">
<span class="relative flex flex-0 items-center justify-center w-12 h-12 mr-6 rounded overflow-hidden border">
<img
class="w-8"
*ngIf="product.thumbnail"
[src]="product.thumbnail">
<span
class="flex items-center justify-center w-full h-full text-xs font-semibold leading-none text-center uppercase"
*ngIf="!product.thumbnail">
No Image
</span>
</span>
<span class="truncate">{{product.sku}}</span>
</div>
</td>
</ng-container>
<!-- Name -->
<ng-container matColumnDef="name">
<th
class="bg-gray-50 dark:bg-black dark:bg-opacity-5"
mat-header-cell
*matHeaderCellDef
mat-sort-header
disableClear>
Name
</th>
<td
class="pr-8 truncate"
mat-cell
*matCellDef="let product">
{{product.name}}
</td>
</ng-container>
<!-- Price -->
<ng-container matColumnDef="price">
<th
class="w-40 bg-gray-50 dark:bg-black dark:bg-opacity-5"
mat-header-cell
*matHeaderCellDef
mat-sort-header
disableClear>
</div>
<div [mat-sort-header]="'name'">Name</div>
<div
class="hidden sm:block"
[mat-sort-header]="'price'">
Price
</th>
<td
class="pr-4"
mat-cell
*matCellDef="let product">
{{product.price | currency:'USD':'symbol':'1.2-2'}}
</td>
</ng-container>
<!-- Stock -->
<ng-container matColumnDef="stock">
<th
class="w-24 bg-gray-50 dark:bg-black dark:bg-opacity-5"
mat-header-cell
*matHeaderCellDef
mat-sort-header
disableClear>
</div>
<div
class="hidden lg:block"
[mat-sort-header]="'stock'">
Stock
</th>
<td
class="pr-4"
mat-cell
*matCellDef="let product">
<span class="flex items-center">
<span class="min-w-4">{{product.stock}}</span>
<!-- Low stock -->
<span
class="flex items-end ml-2 w-1 h-4 bg-red-200 rounded overflow-hidden"
*ngIf="product.stock < 20">
<span class="flex w-full h-1/3 bg-red-600"></span>
</span>
<!-- Medium stock -->
<span
class="flex items-end ml-2 w-1 h-4 bg-orange-200 rounded overflow-hidden"
*ngIf="product.stock >= 20 && product.stock < 30">
<span class="flex w-full h-2/4 bg-orange-400"></span>
</span>
<!-- High stock -->
<span
class="flex items-end ml-2 w-1 h-4 bg-green-100 rounded overflow-hidden"
*ngIf="product.stock >= 30">
<span class="flex w-full h-full bg-green-400"></span>
</span>
</span>
</td>
</ng-container>
<!-- Active -->
<ng-container matColumnDef="active">
<th
class="w-24 bg-gray-50 dark:bg-black dark:bg-opacity-5"
mat-header-cell
*matHeaderCellDef
mat-sort-header
disableClear>
</div>
<div
class="hidden lg:block"
[mat-sort-header]="'active'">
Active
</th>
<td
class="pr-4"
mat-cell
*matCellDef="let product">
<mat-icon
class="text-green-400 icon-size-5"
*ngIf="product.active"
[svgIcon]="'heroicons_solid:check'"></mat-icon>
<mat-icon
class="text-gray-400 icon-size-5"
*ngIf="!product.active"
[svgIcon]="'heroicons_solid:x'"></mat-icon>
</td>
</ng-container>
<!-- Details -->
<ng-container matColumnDef="details">
<th
class="w-24 pr-8 bg-gray-50 dark:bg-black dark:bg-opacity-5"
mat-header-cell
*matHeaderCellDef>
Details
</th>
<td
class="pr-8"
mat-cell
*matCellDef="let product">
<button
class="min-w-10 min-h-7 h-7 px-2 leading-6"
mat-stroked-button
(click)="toggleDetails(product.id)">
<mat-icon
class="icon-size-5"
[svgIcon]="selectedProduct?.id === product.id ? 'heroicons_solid:chevron-up' : 'heroicons_solid:chevron-down'"></mat-icon>
</button>
</td>
</ng-container>
<!-- Product details row -->
<ng-container matColumnDef="productDetails">
<td
class="p-0 border-b-0"
mat-cell
*matCellDef="let product"
[attr.colspan]="productsTableColumns.length">
<ng-container *ngIf="selectedProduct?.id === product.id">
<ng-container *ngTemplateOutlet="rowDetailsTemplate; context: {$implicit: product}"></ng-container>
</ng-container>
</td>
<ng-template
#rowDetailsTemplate
let-product>
<div
class="shadow-lg overflow-hidden"
[@expandCollapse]="selectedProduct?.id === product.id ? 'expanded' : 'collapsed'">
<div class="flex border-b">
<!-- Selected product form -->
<form
class="flex flex-col w-full"
[formGroup]="selectedProductForm">
<div class="flex p-8">
<!-- Product images and status -->
<div class="flex flex-col">
<div class="flex flex-col items-center">
<div class="p-3 border rounded">
<ng-container *ngIf="selectedProductForm.get('images').value.length; else noImage">
<img
class="w-30 min-w-30"
[src]="selectedProductForm.get('images').value[selectedProductForm.get('currentImageIndex').value]">
</ng-container>
<ng-template #noImage>
<span class="flex items-center min-h-20 text-lg font-semibold">NO IMAGE</span>
</ng-template>
</div>
<div
class="flex items-center mt-2"
*ngIf="selectedProductForm.get('images').value.length">
<button
mat-icon-button
(click)="cycleImages(false)">
<mat-icon
class="icon-size-5"
[svgIcon]="'heroicons_solid:arrow-narrow-left'"></mat-icon>
</button>
<span class="font-sm mx-2">
{{selectedProductForm.get('currentImageIndex').value + 1}} of {{selectedProductForm.get('images').value.length}}
</span>
<button
mat-icon-button
(click)="cycleImages(true)">
<mat-icon
class="icon-size-5"
[svgIcon]="'heroicons_solid:arrow-narrow-right'"></mat-icon>
</button>
</div>
</div>
<div class="flex flex-col mt-8">
<span class="font-semibold mb-2">Product status</span>
<mat-slide-toggle
[formControlName]="'active'"
[color]="'primary'">
{{selectedProductForm.get('active').value === true ? 'Active' : 'Disabled'}}
</mat-slide-toggle>
</div>
</div>
<div class="flex flex-auto">
<div class="flex flex-col w-2/4 pl-8">
<!-- Name -->
<mat-form-field class="w-full">
<mat-label>Name</mat-label>
<input
matInput
[formControlName]="'name'">
</mat-form-field>
<!-- SKU and Barcode -->
<div class="flex">
<mat-form-field class="w-1/3 pr-2">
<mat-label>SKU</mat-label>
<input
matInput
[formControlName]="'sku'">
</mat-form-field>
<mat-form-field class="w-2/3 pl-2">
<mat-label>Barcode</mat-label>
<input
matInput
[formControlName]="'barcode'">
</mat-form-field>
</div>
<!-- Category, Brand & Vendor -->
<div class="flex">
<mat-form-field class="w-1/3 pr-2">
<mat-label>Category</mat-label>
<mat-select [formControlName]="'category'">
<ng-container *ngFor="let category of categories">
<mat-option [value]="category.id">
{{category.name}}
</mat-option>
</ng-container>
</mat-select>
</mat-form-field>
<mat-form-field class="w-1/3 px-2">
<mat-label>Brand</mat-label>
<mat-select [formControlName]="'brand'">
<ng-container *ngFor="let brand of brands">
<mat-option [value]="brand.id">
{{brand.name}}
</mat-option>
</ng-container>
</mat-select>
</mat-form-field>
<mat-form-field class="w-1/3 pl-2">
<mat-label>Vendor</mat-label>
<mat-select [formControlName]="'vendor'">
<ng-container *ngFor="let vendor of vendors">
<mat-option [value]="vendor.id">
{{vendor.name}}
</mat-option>
</ng-container>
</mat-select>
</mat-form-field>
</div>
<!-- Stock and Reserved -->
<div class="flex">
<mat-form-field class="w-1/3 pr-2">
<mat-label>Stock</mat-label>
<input
type="number"
matInput
[formControlName]="'stock'">
</mat-form-field>
<mat-form-field class="w-1/3 pl-2">
<mat-label>Reserved</mat-label>
<input
type="number"
matInput
[formControlName]="'reserved'">
</mat-form-field>
</div>
</div>
<!-- Cost, Base price, Tax & Price -->
<div class="flex flex-col w-1/4 pl-8">
<mat-form-field class="w-full">
<mat-label>Cost</mat-label>
<span matPrefix>$</span>
<input
matInput
[formControlName]="'cost'">
</mat-form-field>
<mat-form-field class="w-full">
<mat-label>Base Price</mat-label>
<span matPrefix>$</span>
<input
matInput
[formControlName]="'basePrice'">
</mat-form-field>
<mat-form-field class="w-full">
<mat-label>Tax</mat-label>
<span matSuffix>%</span>
<input
type="number"
matInput
[formControlName]="'taxPercent'">
</mat-form-field>
<mat-form-field class="w-full">
<mat-label>Price</mat-label>
<span matSuffix>$</span>
<input
matInput
[formControlName]="'price'">
</mat-form-field>
</div>
<!-- Weight & Tags -->
<div class="flex flex-col w-1/4 pl-8">
<mat-form-field class="w-full">
<mat-label>Weight</mat-label>
<span matSuffix>lbs.</span>
<input
matInput
[formControlName]="'weight'">
</mat-form-field>
<!-- Tags -->
<ng-container *ngIf="selectedProduct && selectedProduct.tags.length">
<span class="font-semibold">Tags</span>
<div class="mt-1 rounded-md border border-gray-300 shadow-sm overflow-hidden">
<!-- Header -->
<div class="flex items-center my-2 mx-3">
<div class="flex items-center flex-auto min-w-0">
<mat-icon
class="icon-size-5"
[svgIcon]="'heroicons_solid:search'"></mat-icon>
<input
class="min-w-0 ml-2 py-1 border-0"
type="text"
placeholder="Enter tag name"
(input)="filterTags($event)"
(keydown)="filterTagsInputKeyDown($event)"
[maxLength]="50"
#newTagInput>
</div>
<button
class="ml-3 w-8 h-8 min-h-8"
mat-icon-button
(click)="toggleTagsEditMode()">
<mat-icon
*ngIf="!tagsEditMode"
class="icon-size-5"
[svgIcon]="'heroicons_solid:pencil-alt'"></mat-icon>
<mat-icon
*ngIf="tagsEditMode"
class="icon-size-5"
[svgIcon]="'heroicons_solid:check'"></mat-icon>
</button>
</div>
<!-- Available tags -->
<div class="max-h-40 leading-none overflow-y-auto border-t">
<!-- Tags -->
<ng-container *ngIf="!tagsEditMode">
<ng-container *ngFor="let tag of filteredTags; trackBy: trackByFn">
<mat-checkbox
class="flex items-center h-10 min-h-10 px-4"
[color]="'primary'"
[checked]="selectedProduct.tags.includes(tag.id)"
(change)="toggleProductTag(tag, $event)">
{{tag.title}}
</mat-checkbox>
</ng-container>
</ng-container>
<!-- Tags editing -->
<ng-container *ngIf="tagsEditMode">
<div class="p-4 space-y-2">
<ng-container *ngFor="let tag of filteredTags; trackBy: trackByFn">
<mat-form-field class="fuse-mat-dense fuse-mat-no-subscript w-full">
<input
matInput
[value]="tag.title"
(input)="updateTagTitle(tag, $event)">
<button
mat-icon-button
(click)="deleteTag(tag)"
matSuffix>
<mat-icon
class="icon-size-5"
[svgIcon]="'heroicons_solid:trash'"></mat-icon>
</button>
</mat-form-field>
</ng-container>
</div>
</ng-container>
</div>
<div
class="flex items-center h-10 min-h-10 -ml-0.5 pl-4 pr-3 leading-none cursor-pointer border-t hover:bg-gray-50 dark:hover:bg-hover"
*ngIf="shouldShowCreateTagButton(newTagInput.value)"
(click)="createTag(newTagInput.value); newTagInput.value = ''"
matRipple>
<mat-icon
class="mr-2 icon-size-5"
[svgIcon]="'heroicons_solid:plus-circle'"></mat-icon>
<div class="break-all">Create "<b>{{newTagInput.value}}</b>"</div>
</div>
</div>
</ng-container>
</div>
</div>
</div>
<div class="hidden sm:block">Details</div>
</div>
<!-- Rows -->
<ng-container *ngIf="(products$ | async) as products">
<ng-container *ngFor="let product of products; trackBy: trackByFn">
<div class="inventory-grid grid items-center gap-4 py-3 px-6 md:px-8 border-b">
<!-- Image -->
<div class="flex items-center">
<div class="relative flex flex-0 items-center justify-center w-12 h-12 mr-6 rounded overflow-hidden border">
<img
class="w-8"
*ngIf="product.thumbnail"
[alt]="'Product thumbnail image'"
[src]="product.thumbnail">
<div
class="flex items-center justify-center w-full h-full text-xs font-semibold leading-none text-center uppercase"
*ngIf="!product.thumbnail">
NO THUMB
</div>
</div>
</div>
<div class="flex items-center justify-between w-full border-t px-8 py-4">
<button
class="-ml-4"
mat-button
[color]="'warn'"
(click)="deleteSelectedProduct()">
Delete
</button>
<div class="flex items-center">
<div
class="flex items-center mr-4"
*ngIf="flashMessage">
<ng-container *ngIf="flashMessage === 'success'">
<mat-icon
class="text-green-500"
[svgIcon]="'heroicons_outline:check'"></mat-icon>
<span class="ml-2">Product updated</span>
</ng-container>
<ng-container *ngIf="flashMessage === 'error'">
<mat-icon
class="text-red-500"
[svgIcon]="'heroicons_outline:x'"></mat-icon>
<span class="ml-2">An error occurred, try again!</span>
</ng-container>
</div>
<button
mat-flat-button
[color]="'primary'"
(click)="updateSelectedProduct()">
Update
</button>
</div>
</div>
<!-- SKU -->
<div class="hidden md:block truncate">
{{product.sku}}
</div>
</form>
<!-- Name -->
<div class="truncate">
{{product.name}}
</div>
<!-- Price -->
<div class="hidden sm:block">
{{product.price | currency:'USD':'symbol':'1.2-2'}}
</div>
<!-- Stock -->
<div class="hidden lg:flex items-center">
<div class="min-w-4">{{product.stock}}</div>
<!-- Low stock -->
<div
class="flex items-end ml-2 w-1 h-4 bg-red-200 rounded overflow-hidden"
*ngIf="product.stock < 20">
<div class="flex w-full h-1/3 bg-red-600"></div>
</div>
<!-- Medium stock -->
<div
class="flex items-end ml-2 w-1 h-4 bg-orange-200 rounded overflow-hidden"
*ngIf="product.stock >= 20 && product.stock < 30">
<div class="flex w-full h-2/4 bg-orange-400"></div>
</div>
<!-- High stock -->
<div
class="flex items-end ml-2 w-1 h-4 bg-green-100 rounded overflow-hidden"
*ngIf="product.stock >= 30">
<div class="flex w-full h-full bg-green-400"></div>
</div>
</div>
<!-- Active -->
<div class="hidden lg:block">
<ng-container *ngIf="product.active">
<mat-icon
class="text-green-400 icon-size-5"
[svgIcon]="'heroicons_solid:check'"></mat-icon>
</ng-container>
<ng-container *ngIf="!product.active">
<mat-icon
class="text-gray-400 icon-size-5"
[svgIcon]="'heroicons_solid:x'"></mat-icon>
</ng-container>
</div>
<!-- Details button -->
<div>
<button
class="min-w-10 min-h-7 h-7 px-2 leading-6"
mat-stroked-button
(click)="toggleDetails(product.id)">
<mat-icon
class="icon-size-5"
[svgIcon]="selectedProduct?.id === product.id ? 'heroicons_solid:chevron-up' : 'heroicons_solid:chevron-down'"></mat-icon>
</button>
</div>
</div>
</ng-template>
<div class="grid">
<ng-container *ngIf="selectedProduct?.id === product.id">
<ng-container *ngTemplateOutlet="rowDetailsTemplate; context: {$implicit: product}"></ng-container>
</ng-container>
</div>
</ng-container>
</ng-container>
</div>
<tr
class="shadow"
mat-header-row
*matHeaderRowDef="productsTableColumns; sticky: true"></tr>
<tr
class="h-18 hover:bg-gray-100 dark:hover:bg-hover"
mat-row
*matRowDef="let product; columns: productsTableColumns;"></tr>
<tr
class="h-0"
mat-row
*matRowDef="let row; columns: ['productDetails']"></tr>
</table>
</div>
<mat-paginator
class="sm:absolute sm:inset-x-0 sm:bottom-0 border-b sm:border-t sm:border-b-0 z-10 bg-gray-50 dark:bg-transparent"
[ngClass]="{'pointer-events-none': isLoading}"
[length]="pagination.length"
[pageIndex]="pagination.page"
[pageSize]="pagination.size"
[pageSizeOptions]="[5, 10, 25, 100]"
[showFirstLastButtons]="true"></mat-paginator>
<mat-paginator
class="sm:absolute sm:inset-x-0 sm:bottom-0 border-b sm:border-t sm:border-b-0 z-10 bg-gray-50 dark:bg-transparent"
[ngClass]="{'pointer-events-none': isLoading}"
[length]="pagination.length"
[pageIndex]="pagination.page"
[pageSize]="pagination.size"
[pageSizeOptions]="[5, 10, 25, 100]"
[showFirstLastButtons]="true"></mat-paginator>
</ng-container>
</ng-container>
<ng-template
#rowDetailsTemplate
let-product>
<div class="shadow-lg overflow-hidden">
<div class="flex border-b">
<!-- Selected product form -->
<form
class="flex flex-col w-full"
[formGroup]="selectedProductForm">
<div class="flex flex-col sm:flex-row p-8">
<!-- Product images and status -->
<div class="flex flex-col items-center sm:items-start mb-8 sm:mb-0">
<div class="flex flex-col items-center">
<div class="w-32 h-44 border rounded overflow-hidden">
<ng-container *ngIf="selectedProductForm.get('images').value.length; else noImage">
<img
class="w-full h-full object-cover"
[src]="selectedProductForm.get('images').value[selectedProductForm.get('currentImageIndex').value]">
</ng-container>
<ng-template #noImage>
<span class="flex items-center min-h-20 text-lg font-semibold">NO IMAGE</span>
</ng-template>
</div>
<div
class="flex items-center mt-2 whitespace-nowrap"
*ngIf="selectedProductForm.get('images').value.length">
<button
mat-icon-button
(click)="cycleImages(false)">
<mat-icon
class="icon-size-5"
[svgIcon]="'heroicons_solid:arrow-narrow-left'"></mat-icon>
</button>
<span class="font-sm mx-2">
{{selectedProductForm.get('currentImageIndex').value + 1}} of {{selectedProductForm.get('images').value.length}}
</span>
<button
mat-icon-button
(click)="cycleImages(true)">
<mat-icon
class="icon-size-5"
[svgIcon]="'heroicons_solid:arrow-narrow-right'"></mat-icon>
</button>
</div>
</div>
<div class="flex flex-col mt-8">
<span class="font-semibold mb-2">Product status</span>
<mat-slide-toggle
[formControlName]="'active'"
[color]="'primary'">
{{selectedProductForm.get('active').value === true ? 'Active' : 'Disabled'}}
</mat-slide-toggle>
</div>
</div>
<div class="flex flex-auto flex-wrap">
<!-- Name, SKU & etc. -->
<div class="flex flex-col w-full lg:w-2/4 sm:pl-8">
<!-- Name -->
<mat-form-field class="w-full">
<mat-label>Name</mat-label>
<input
matInput
[formControlName]="'name'">
</mat-form-field>
<!-- SKU and Barcode -->
<div class="flex">
<mat-form-field class="w-1/3 pr-2">
<mat-label>SKU</mat-label>
<input
matInput
[formControlName]="'sku'">
</mat-form-field>
<mat-form-field class="w-2/3 pl-2">
<mat-label>Barcode</mat-label>
<input
matInput
[formControlName]="'barcode'">
</mat-form-field>
</div>
<!-- Category, Brand & Vendor -->
<div class="flex">
<mat-form-field class="w-1/3 pr-2">
<mat-label>Category</mat-label>
<mat-select [formControlName]="'category'">
<ng-container *ngFor="let category of categories">
<mat-option [value]="category.id">
{{category.name}}
</mat-option>
</ng-container>
</mat-select>
</mat-form-field>
<mat-form-field class="w-1/3 px-2">
<mat-label>Brand</mat-label>
<mat-select [formControlName]="'brand'">
<ng-container *ngFor="let brand of brands">
<mat-option [value]="brand.id">
{{brand.name}}
</mat-option>
</ng-container>
</mat-select>
</mat-form-field>
<mat-form-field class="w-1/3 pl-2">
<mat-label>Vendor</mat-label>
<mat-select [formControlName]="'vendor'">
<ng-container *ngFor="let vendor of vendors">
<mat-option [value]="vendor.id">
{{vendor.name}}
</mat-option>
</ng-container>
</mat-select>
</mat-form-field>
</div>
<!-- Stock and Reserved -->
<div class="flex">
<mat-form-field class="w-1/3 pr-2">
<mat-label>Stock</mat-label>
<input
type="number"
matInput
[formControlName]="'stock'">
</mat-form-field>
<mat-form-field class="w-1/3 pl-2">
<mat-label>Reserved</mat-label>
<input
type="number"
matInput
[formControlName]="'reserved'">
</mat-form-field>
</div>
</div>
<!-- Cost, Base price, Tax & Price -->
<div class="flex flex-col w-full lg:w-1/4 sm:pl-8">
<mat-form-field class="w-full">
<mat-label>Cost</mat-label>
<span matPrefix>$</span>
<input
matInput
[formControlName]="'cost'">
</mat-form-field>
<mat-form-field class="w-full">
<mat-label>Base Price</mat-label>
<span matPrefix>$</span>
<input
matInput
[formControlName]="'basePrice'">
</mat-form-field>
<mat-form-field class="w-full">
<mat-label>Tax</mat-label>
<span matSuffix>%</span>
<input
type="number"
matInput
[formControlName]="'taxPercent'">
</mat-form-field>
<mat-form-field class="w-full">
<mat-label>Price</mat-label>
<span matSuffix>$</span>
<input
matInput
[formControlName]="'price'">
</mat-form-field>
</div>
<!-- Weight & Tags -->
<div class="flex flex-col w-full lg:w-1/4 sm:pl-8">
<mat-form-field class="w-full">
<mat-label>Weight</mat-label>
<span matSuffix>lbs.</span>
<input
matInput
[formControlName]="'weight'">
</mat-form-field>
<!-- Tags -->
<span class="mb-px font-medium leading-tight">Tags</span>
<div class="mt-1.5 rounded-md border border-gray-300 dark:border-gray-500 shadow-sm overflow-hidden">
<!-- Header -->
<div class="flex items-center -my-px py-2 px-3">
<div class="flex items-center flex-auto min-w-0">
<mat-icon
class="icon-size-5"
[svgIcon]="'heroicons_solid:search'"></mat-icon>
<input
class="min-w-0 ml-2 py-1 border-0"
type="text"
placeholder="Enter tag name"
(input)="filterTags($event)"
(keydown)="filterTagsInputKeyDown($event)"
[maxLength]="50"
#newTagInput>
</div>
<button
class="ml-3 w-8 h-8 min-h-8"
mat-icon-button
(click)="toggleTagsEditMode()">
<mat-icon
*ngIf="!tagsEditMode"
class="icon-size-5"
[svgIcon]="'heroicons_solid:pencil-alt'"></mat-icon>
<mat-icon
*ngIf="tagsEditMode"
class="icon-size-5"
[svgIcon]="'heroicons_solid:check'"></mat-icon>
</button>
</div>
<!-- Available tags -->
<div class="h-44 leading-none overflow-y-auto border-t border-gray-300 dark:border-gray-500">
<!-- Tags -->
<ng-container *ngIf="!tagsEditMode">
<ng-container *ngFor="let tag of filteredTags; trackBy: trackByFn">
<mat-checkbox
class="flex items-center h-10 min-h-10 px-4"
[color]="'primary'"
[checked]="selectedProduct.tags.includes(tag.id)"
(change)="toggleProductTag(tag, $event)">
{{tag.title}}
</mat-checkbox>
</ng-container>
</ng-container>
<!-- Tags editing -->
<ng-container *ngIf="tagsEditMode">
<div class="p-4 space-y-2">
<ng-container *ngFor="let tag of filteredTags; trackBy: trackByFn">
<mat-form-field class="fuse-mat-dense fuse-mat-no-subscript w-full">
<input
matInput
[value]="tag.title"
(input)="updateTagTitle(tag, $event)">
<button
mat-icon-button
(click)="deleteTag(tag)"
matSuffix>
<mat-icon
class="icon-size-5"
[svgIcon]="'heroicons_solid:trash'"></mat-icon>
</button>
</mat-form-field>
</ng-container>
</div>
</ng-container>
<div
class="flex items-center h-10 min-h-10 -ml-0.5 pl-4 pr-3 leading-none cursor-pointer border-t hover:bg-gray-50 dark:hover:bg-hover"
*ngIf="shouldShowCreateTagButton(newTagInput.value)"
(click)="createTag(newTagInput.value); newTagInput.value = ''"
matRipple>
<mat-icon
class="mr-2 icon-size-5"
[svgIcon]="'heroicons_solid:plus-circle'"></mat-icon>
<div class="break-all">Create "<b>{{newTagInput.value}}</b>"</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="flex items-center justify-between w-full border-t px-8 py-4">
<button
class="-ml-4"
mat-button
[color]="'warn'"
(click)="deleteSelectedProduct()">
Delete
</button>
<div class="flex items-center">
<div
class="flex items-center mr-4"
*ngIf="flashMessage">
<ng-container *ngIf="flashMessage === 'success'">
<mat-icon
class="text-green-500"
[svgIcon]="'heroicons_outline:check'"></mat-icon>
<span class="ml-2">Product updated</span>
</ng-container>
<ng-container *ngIf="flashMessage === 'error'">
<mat-icon
class="text-red-500"
[svgIcon]="'heroicons_outline:x'"></mat-icon>
<span class="ml-2">An error occurred, try again!</span>
</ng-container>
</div>
<button
mat-flat-button
[color]="'primary'"
(click)="updateSelectedProduct()">
Update
</button>
</div>
</div>
</form>
</div>
</div>
</ng-template>
<ng-template #noProducts>
<div class="p-8 sm:p-16 border-t text-4xl font-semibold tracking-tight text-center">There are no products!</div>
</ng-template>

View File

@@ -12,6 +12,26 @@ import { InventoryService } from 'app/modules/admin/apps/ecommerce/inventory/inv
@Component({
selector : 'inventory-list',
templateUrl : './inventory.component.html',
styles : [
/* language=SCSS */
`
.inventory-grid {
grid-template-columns: 48px auto 40px;
@screen sm {
grid-template-columns: 48px auto 112px 72px;
}
@screen md {
grid-template-columns: 48px 112px auto 112px 72px;
}
@screen lg {
grid-template-columns: 48px 112px auto 112px 96px 96px 72px;
}
}
`
],
encapsulation : ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
animations : fuseAnimations
@@ -29,8 +49,6 @@ export class InventoryListComponent implements OnInit, AfterViewInit, OnDestroy
flashMessage: 'success' | 'error' | null = null;
isLoading: boolean = false;
pagination: InventoryPagination;
productsCount: number = 0;
productsTableColumns: string[] = ['sku', 'name', 'price', 'stock', 'active', 'details'];
searchInputControl: FormControl = new FormControl();
selectedProduct: InventoryProduct | null = null;
selectedProductForm: FormGroup;
@@ -121,16 +139,6 @@ export class InventoryListComponent implements OnInit, AfterViewInit, OnDestroy
// Get the products
this.products$ = this._inventoryService.products$;
this._inventoryService.products$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((products: InventoryProduct[]) => {
// Update the counts
this.productsCount = products.length;
// Mark for check
this._changeDetectorRef.markForCheck();
});
// Get the tags
this._inventoryService.tags$
@@ -179,28 +187,41 @@ export class InventoryListComponent implements OnInit, AfterViewInit, OnDestroy
*/
ngAfterViewInit(): void
{
// If the user changes the sort order...
this._sort.sortChange
.pipe(takeUntil(this._unsubscribeAll))
.subscribe(() => {
// Reset back to the first page
this._paginator.pageIndex = 0;
// Close the details
this.closeDetails();
if ( this._sort && this._paginator )
{
// Set the initial sort
this._sort.sort({
id : 'name',
start : 'asc',
disableClear: true
});
// Get products if sort or page changes
merge(this._sort.sortChange, this._paginator.page).pipe(
switchMap(() => {
this.closeDetails();
this.isLoading = true;
return this._inventoryService.getProducts(this._paginator.pageIndex, this._paginator.pageSize, this._sort.active, this._sort.direction);
}),
map(() => {
this.isLoading = false;
})
).subscribe();
// Mark for check
this._changeDetectorRef.markForCheck();
// If the user changes the sort order...
this._sort.sortChange
.pipe(takeUntil(this._unsubscribeAll))
.subscribe(() => {
// Reset back to the first page
this._paginator.pageIndex = 0;
// Close the details
this.closeDetails();
});
// Get products if sort or page changes
merge(this._sort.sortChange, this._paginator.page).pipe(
switchMap(() => {
this.closeDetails();
this.isLoading = true;
return this._inventoryService.getProducts(this._paginator.pageIndex, this._paginator.pageSize, this._sort.active, this._sort.direction);
}),
map(() => {
this.isLoading = false;
})
).subscribe();
}
}
/**
@@ -332,15 +353,15 @@ export class InventoryListComponent implements OnInit, AfterViewInit, OnDestroy
const tag = this.filteredTags[0];
const isTagApplied = this.selectedProduct.tags.find(id => id === tag.id);
// If the found tag is already applied to the contact...
// If the found tag is already applied to the product...
if ( isTagApplied )
{
// Remove the tag from the contact
// Remove the tag from the product
this.removeTagFromProduct(tag);
}
else
{
// Otherwise add the tag to the contact
// Otherwise add the tag to the product
this.addTagToProduct(tag);
}
}

View File

@@ -4,7 +4,7 @@
<div class="flex items-center justify-end">
<button
mat-icon-button
[routerLink]="['../']">
[routerLink]="['../../']">
<mat-icon [svgIcon]="'heroicons_outline:x'"></mat-icon>
</button>
</div>

View File

@@ -22,17 +22,17 @@ export class CanDeactivateFileManagerDetails implements CanDeactivate<FileManage
nextRoute = nextRoute.firstChild;
}
// If the next state doesn't contain '/files'
// If the next state doesn't contain '/file-manager'
// it means we are navigating away from the
// tasks app
// file manager app
if ( !nextState.url.includes('/file-manager') )
{
// Let it navigate
return true;
}
// If we are navigating to another task...
if ( nextRoute.paramMap.get('id') )
// If we are navigating to another item...
if ( nextState.url.includes('/details') )
{
// Just navigate
return true;

Some files were not shown because too many files have changed in this diff Show More