Compare commits

..

52 Commits

Author SHA1 Message Date
sercan
3395e7f0bc (docs) Updated docs 2021-05-15 13:44:13 +03:00
sercan
ee1caef303 Increased the version number
(changelog) Updated changelog
2021-05-14 17:44:18 +03:00
sercan
cad136c0e5 (dependencies) Updated packages 2021-05-14 17:26:28 +03:00
sercan
d159ae1458 (linting) Migrated over to the ESLint 2021-05-14 17:17:06 +03:00
sercan
0c5f5b9165 (global) "ng build" automatically builds into production mode by default 2021-05-14 14:15:54 +03:00
sercan
f4ca06a9a8 (dependencies) Updated Angular Material to v12.0.0 2021-05-14 14:15:14 +03:00
sercan
6a86deaeec (dependencies) Updated Angular to v12.0.0 2021-05-14 12:12:24 +03:00
sercan
44e7401310 (pages/settings) Moved titles to the same line with sidebar toggle button + small alignment tweaks 2021-05-12 15:49:36 +03:00
sercan
6d1dee8d0d (3rdParty) Fixed: "_redirects" file must be in /src folder 2021-05-12 14:47:53 +03:00
sercan
b8b3d1daab (3rdParty) Added a _redirects file for Netlify support 2021-05-12 14:28:16 +03:00
sercan
c3ed184853 (pages/settings) Added a close button for the sidebar 2021-05-12 14:14:03 +03:00
sercan
a9ada174b4 (routing) Use "corrected" behavior for relative link resolution (https://github.com/angular/angular/pull/22394) as it's the default value starting from Angular v11 (https://github.com/angular/angular/pull/25609) 2021-05-12 12:46:01 +03:00
sercan
57ba071fa5 (pages/settings) Fixed: Drawer shouldn't be closed on 'side' mode when changing the selected panel 2021-05-11 13:09:38 +03:00
sercan
cc761d58e5 (pages/settings) Added new Settings page 2021-05-11 12:48:51 +03:00
sercan
5c3db88908 (apps/ecommerce) Tweaked hover color on inventory list for better consistency 2021-05-07 12:43:04 +03:00
sercan
039eef98ee (apps/chat) Tweaked hover colors on lists for better consistency 2021-05-07 12:40:02 +03:00
sercan
83e67e1286 (docs/changelog) Small visual improvement on changelog titles 2021-05-07 12:33:50 +03:00
sercan
2bea991ba3 (refactoring) Move *ngFor directives to their own <ng-container> elements. This is mostly a personal preference but it's a good habit to have as you cannot put more than one structural directive on a single element. This way our main repeated element is free of any common structural directives so we can use ours on them if needed. 2021-05-07 12:10:45 +03:00
sercan
7e430a269c (apps/tasks) Visual improvements
(apps/contacts) Tweaked the hover color on contacts list for better consistency
2021-05-07 11:54:47 +03:00
sercan
4ccce1b423 Increased the version number
(changelog) Updated the changelog
2021-05-06 17:11:12 +03:00
sercan
f6b4ca0880 (apps/notes) Responsive adjustments 2021-05-06 17:05:56 +03:00
sercan
77014174e8 (apps/notes) New version of the Notes app 2021-05-06 17:01:14 +03:00
sercan
5ac7002a98 (fuse/masonry) Fixed: Masonry doesn't work with the data that comes from async pipe 2021-05-06 12:07:07 +03:00
sercan
b0f1e1de95 (apps/mailbox) App title font size adjustment for better consistency 2021-05-05 17:52:10 +03:00
sercan
cf01383358 (apps/mailbox) Use shadow on threads for better consistency 2021-05-05 17:27:39 +03:00
sercan
e4442d683b (apps/tasks) Tweaked the hover color on task list for better consistency 2021-05-05 17:27:12 +03:00
sercan
623b43a94c (fuse/styles) Fixed: fuse-highlight doesn't have a margin around in Docs 2021-05-03 18:49:00 +03:00
sercan
e7a1d386a6 (fuse/masonry) Added a new component (and its docs) for creating Masonry layouts 2021-05-03 18:47:57 +03:00
sercan
b05763135e (apps/chat) Adjustments and optimizations for smaller devices 2021-04-30 19:55:37 +03:00
sercan
5dd60c816c (apps/chat) Small adjustments and tweaks for Dark mode 2021-04-30 19:39:02 +03:00
sercan
0ac967a945 Removed optional chaining operators to support Node v12
Set the version on .nvmrc to 12
2021-04-30 19:27:40 +03:00
sercan
e3821da077 (apps/chat) New and improved Chat app 2021-04-30 19:18:09 +03:00
sercan
ee48e11548 Increased the version number
(dependencies) Updated Angular, Angular Material and various other packages
(changelog) Updated the changelog
2021-04-30 19:07:53 +03:00
sercan
215546cc31 (apps/academy) Removed a misplaced import 2021-04-29 19:57:58 +03:00
sercan
072dbce6d4 (fuse/fullscreen) Added Fullscreen toggle component 2021-04-28 10:55:32 +03:00
sercan
5ffe0d0efa (pages/pricing) Improved the spacing of the CTA section on all pricing pages 2021-04-26 23:42:16 +03:00
sercan
e90fb9e618 (apps/academy) Added missing trackBy functions to '*ngFor's 2021-04-26 16:41:21 +03:00
sercan
88e98d002d (apps/mailbox) Removed unused methods 2021-04-26 16:08:19 +03:00
sercan
deeef323f9 (apps/academy) Better error handling on courses that are not exist 2021-04-26 15:59:44 +03:00
sercan
284e282761 Remove quotes from .nvmrc version info 2021-04-26 10:23:08 +03:00
sercan
52e234325f Added .nvmrc with required Node version 2021-04-26 10:19:52 +03:00
sercan
42e0864538 (dependencies) Updated Angular, Angular Material and various other packages
(changelog) Added changelog and updated the version number
2021-04-26 09:55:41 +03:00
sercan
6b6442b37f (apps/academy) Added page info in between the desktop navigation buttons 2021-04-26 09:29:59 +03:00
sercan
bb0efade72 (apps/academy) New version of the Academy app 2021-04-25 23:23:23 +03:00
sercan
63edc8d1f2 (layouts/classy) Disabled footer on Classy layout for more 'classy' look 2021-04-25 23:15:38 +03:00
sercan
9dde624bb5 (icons) Added Material Solid icons 2021-04-25 23:14:13 +03:00
sercan
a5a27d0a51 (icons) Added Material Solid icons 2021-04-25 13:23:26 +03:00
sercan
85ea34a6ce (Contacts) Fixed: Clicking on the tag checkbox on tag panel breaks the toggling 2021-04-25 12:42:41 +03:00
sercan
9b059f8d0d (icons/heroicons) Updated heroicons to v1.0.1 2021-04-22 22:05:59 +03:00
sercan
df48ad1c56 (apps/file-manager) Removed unnecessary imports 2021-04-22 11:24:16 +03:00
sercan
6a113a5317 (icons/heroicons-outline) Fixed: academic-cap icon path fills breaks the icon 2021-04-22 11:23:21 +03:00
sercan
4b268e5d1b (apps/file-manager) Better grid 2021-04-20 22:44:12 +03:00
323 changed files with 28175 additions and 21168 deletions

95
.eslintrc.json Normal file
View File

@@ -0,0 +1,95 @@
{
"root": true,
"env": {
"es6": true
},
"parserOptions": {
"ecmaVersion": 2018
},
"ignorePatterns": [
"projects/**/*"
],
"overrides": [
{
"files": [
"*.ts"
],
"parserOptions": {
"project": [
"tsconfig.json",
"e2e/tsconfig.json"
],
"createDefaultProgram": true
},
"extends": [
"plugin:@angular-eslint/ng-cli-compat",
"plugin:@angular-eslint/ng-cli-compat--formatting-add-on",
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "",
"style": "kebab-case"
}
],
"@angular-eslint/directive-selector": [
"error",
{
"type": "attribute",
"prefix": "",
"style": "camelCase"
}
],
"@typescript-eslint/dot-notation": "off",
"@typescript-eslint/explicit-member-accessibility": [
"off",
{
"accessibility": "explicit"
}
],
"@typescript-eslint/no-inferrable-types": "off",
"arrow-parens": [
"error",
"as-needed",
{
"requireForBlockBody": true
}
],
"brace-style": [
"off",
"off"
],
"import/order": "off",
"max-len": [
"error",
{
"ignorePattern": "^import |^export | implements",
"code": 180
}
],
"no-underscore-dangle": "off",
"object-shorthand": "off",
"quote-props": [
"error",
"consistent"
],
"quotes": [
"error",
"single"
]
}
},
{
"files": [
"*.html"
],
"extends": [
"plugin:@angular-eslint/template/recommended"
],
"rules": {}
}
]
}

1
.gitignore vendored
View File

@@ -12,7 +12,6 @@
# profiling files
chrome-profiler-events*.json
speed-measure-plugin*.json
# IDEs and editors
/.idea

1
.nvmrc Normal file
View File

@@ -0,0 +1 @@
12

View File

@@ -1,5 +1,7 @@
# Fuse - Admin template and Starter project for Angular
This project was generated with [Angular CLI](https://github.com/angular/angular-cli)
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
@@ -18,8 +20,8 @@ Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
Run `ng e2e` to execute the end-to-end tests via a platform of your choice.
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.

View File

@@ -22,7 +22,7 @@
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": true,
"inlineStyleLanguage": "scss",
"allowedCommonJsDependencies": [
"apexcharts",
"highlight.js",
@@ -33,7 +33,12 @@
"assets": [
"src/favicon-16x16.png",
"src/favicon-32x32.png",
"src/assets"
"src/assets",
{
"glob": "_redirects",
"input": "src",
"output": "/"
}
],
"stylePreprocessorOptions": {
"includePaths": [
@@ -52,19 +57,6 @@
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
@@ -76,20 +68,37 @@
"maximumWarning": "100kb",
"maximumError": "150kb"
}
]
],
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"outputHashing": "all"
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
}
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "fuse:build"
},
"configurations": {
"production": {
"browserTarget": "fuse:build:production"
},
"development": {
"browserTarget": "fuse:build:development"
}
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
@@ -104,6 +113,7 @@
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"inlineStyleLanguage": "scss",
"assets": [
"src/favicon-16x16.png",
"src/favicon-32x32.png",
@@ -116,15 +126,11 @@
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"builder": "@angular-eslint/builder:lint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
"lintFilePatterns": [
"src/**/*.ts",
"src/**/*.html"
]
}
},
@@ -143,5 +149,8 @@
}
}
},
"defaultProject": "fuse"
"defaultProject": "fuse",
"cli": {
"defaultCollection": "@angular-eslint/schematics"
}
}

View File

@@ -1,40 +0,0 @@
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const {SpecReporter, StacktraceOption} = require('jasmine-spec-reporter');
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout : 11000,
specs : [
'./src/**/*.e2e-spec.ts'
],
capabilities : {
browserName: 'chrome'
},
directConnect : true,
SELENIUM_PROMISE_MANAGER: false,
baseUrl : 'http://localhost:4200/',
framework : 'jasmine',
jasmineNodeOpts : {
showColors : true,
defaultTimeoutInterval: 30000,
print : function ()
{
}
},
onPrepare()
{
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
});
jasmine.getEnv().addReporter(new SpecReporter({
spec: {
displayStacktrace: StacktraceOption.PRETTY
}
}));
}
};

View File

@@ -1,23 +0,0 @@
import { AppPage } from './app.po';
import { browser, logging } from 'protractor';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', async () => {
await page.navigateTo();
expect(await page.getTitleText()).toEqual('Welcome to Fuse!');
});
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE
} as logging.Entry));
});
});

View File

@@ -1,14 +0,0 @@
import { browser, by, element } from 'protractor';
export class AppPage
{
async navigateTo(): Promise<unknown>
{
return browser.get(browser.baseUrl);
}
async getTitleText(): Promise<string>
{
return element(by.css('app-root h1')).getText();
}
}

View File

@@ -1,13 +0,0 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es2018",
"types": [
"jasmine",
"node"
]
}
}

View File

@@ -26,7 +26,7 @@ module.exports = function (config)
suppressAll: true // removes the duplicated traces
},
coverageReporter : {
dir : require('path').join(__dirname, './coverage/angular11'),
dir : require('path').join(__dirname, './coverage/angular12'),
subdir : '.',
reporters: [
{type: 'html'},

8923
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,28 +1,28 @@
{
"name": "@fuse/demo",
"version": "12.0.0",
"version": "13.0.0",
"license": "https://themeforest.net/licenses/standard",
"private": true,
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build --prod",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"dependencies": {
"@angular/animations": "11.2.10",
"@angular/cdk": "11.2.9",
"@angular/common": "11.2.10",
"@angular/compiler": "11.2.10",
"@angular/core": "11.2.10",
"@angular/forms": "11.2.10",
"@angular/material": "11.2.9",
"@angular/material-moment-adapter": "11.2.9",
"@angular/platform-browser": "11.2.10",
"@angular/platform-browser-dynamic": "11.2.10",
"@angular/router": "11.2.10",
"@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",
"@fullcalendar/angular": "4.4.5-beta",
"@fullcalendar/core": "4.4.2",
"@fullcalendar/daygrid": "4.4.2",
@@ -31,53 +31,61 @@
"@fullcalendar/moment": "4.4.2",
"@fullcalendar/rrule": "4.4.2",
"@fullcalendar/timegrid": "4.4.2",
"apexcharts": "3.26.0",
"apexcharts": "3.26.2",
"crypto-js": "3.3.0",
"highlight.js": "10.7.2",
"lodash-es": "4.17.21",
"moment": "2.29.1",
"ng-apexcharts": "1.5.9",
"ngx-markdown": "11.1.2",
"ngx-quill": "13.2.0",
"perfect-scrollbar": "1.5.0",
"ngx-markdown": "11.1.3",
"ngx-quill": "14.0.0",
"perfect-scrollbar": "1.5.1",
"quill": "1.3.7",
"rrule": "2.6.8",
"rxjs": "6.6.7",
"tslib": "2.1.0",
"tslib": "2.2.0",
"web-animations-js": "2.3.2",
"zone.js": "0.11.4"
},
"devDependencies": {
"@angular-devkit/build-angular": "0.1102.9",
"@angular/cli": "11.2.9",
"@angular/compiler-cli": "11.2.10",
"@angular/language-service": "11.2.10",
"@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",
"@types/chroma-js": "2.1.3",
"@types/crypto-js": "3.1.47",
"@types/highlight.js": "10.1.0",
"@types/jasmine": "3.6.8",
"@types/lodash": "4.14.168",
"@types/jasmine": "3.6.11",
"@types/lodash": "4.14.169",
"@types/lodash-es": "4.17.4",
"@types/node": "12.20.6",
"@types/node": "12.20.13",
"@typescript-eslint/eslint-plugin": "4.23.0",
"@typescript-eslint/parser": "4.23.0",
"autoprefixer": "10.2.5",
"chroma-js": "2.1.1",
"codelyzer": "6.0.1",
"jasmine-core": "3.6.0",
"eslint": "7.26.0",
"eslint-plugin-import": "2.23.0",
"eslint-plugin-jsdoc": "34.2.2",
"eslint-plugin-prefer-arrow": "1.2.3",
"jasmine-core": "3.7.1",
"jasmine-spec-reporter": "5.0.2",
"karma": "6.1.2",
"karma": "6.3.2",
"karma-chrome-launcher": "3.1.0",
"karma-coverage": "2.0.3",
"karma-jasmine": "4.0.1",
"karma-jasmine-html-reporter": "1.5.4",
"karma-jasmine-html-reporter": "1.6.0",
"lodash": "4.17.21",
"postcss": "8.2.10",
"postcss": "8.2.15",
"protractor": "7.0.0",
"tailwindcss": "2.1.1",
"ts-node": "8.3.0",
"tslint": "6.1.3",
"typescript": "4.1.5"
"tailwindcss": "2.1.2",
"typescript": "4.2.4"
}
}

View File

@@ -1,14 +1,14 @@
export class FuseAnimationCurves
{
static STANDARD_CURVE = 'cubic-bezier(0.4, 0.0, 0.2, 1)';
static DECELERATION_CURVE = 'cubic-bezier(0.0, 0.0, 0.2, 1)';
static ACCELERATION_CURVE = 'cubic-bezier(0.4, 0.0, 1, 1)';
static SHARP_CURVE = 'cubic-bezier(0.4, 0.0, 0.6, 1)';
static standard = 'cubic-bezier(0.4, 0.0, 0.2, 1)';
static deceleration = 'cubic-bezier(0.0, 0.0, 0.2, 1)';
static acceleration = 'cubic-bezier(0.4, 0.0, 1, 1)';
static sharp = 'cubic-bezier(0.4, 0.0, 0.6, 1)';
}
export class FuseAnimationDurations
{
static COMPLEX = '375ms';
static ENTERING = '225ms';
static EXITING = '195ms';
static complex = '375ms';
static entering = '225ms';
static exiting = '195ms';
}

View File

@@ -24,7 +24,7 @@ const expandCollapse = trigger('expandCollapse',
animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.ENTERING} ${FuseAnimationCurves.DECELERATION_CURVE}`
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
}
}
)

View File

@@ -25,7 +25,7 @@ const fadeIn = trigger('fadeIn',
transition('void => *', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.ENTERING} ${FuseAnimationCurves.DECELERATION_CURVE}`
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
}
}
)
@@ -58,7 +58,7 @@ const fadeInTop = trigger('fadeInTop',
transition('void => *', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.ENTERING} ${FuseAnimationCurves.DECELERATION_CURVE}`
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
}
}
)
@@ -91,7 +91,7 @@ const fadeInBottom = trigger('fadeInBottom',
transition('void => *', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.ENTERING} ${FuseAnimationCurves.DECELERATION_CURVE}`
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
}
}
)
@@ -124,7 +124,7 @@ const fadeInLeft = trigger('fadeInLeft',
transition('void => *', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.ENTERING} ${FuseAnimationCurves.DECELERATION_CURVE}`
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
}
}
)
@@ -157,7 +157,7 @@ const fadeInRight = trigger('fadeInRight',
transition('void => *', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.ENTERING} ${FuseAnimationCurves.DECELERATION_CURVE}`
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
}
}
)
@@ -188,7 +188,7 @@ const fadeOut = trigger('fadeOut',
transition('* => void', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.EXITING} ${FuseAnimationCurves.ACCELERATION_CURVE}`
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
}
}
)
@@ -221,7 +221,7 @@ const fadeOutTop = trigger('fadeOutTop',
transition('* => void', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.EXITING} ${FuseAnimationCurves.ACCELERATION_CURVE}`
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
}
}
)
@@ -254,7 +254,7 @@ const fadeOutBottom = trigger('fadeOutBottom',
transition('* => void', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.EXITING} ${FuseAnimationCurves.ACCELERATION_CURVE}`
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
}
}
)
@@ -287,7 +287,7 @@ const fadeOutLeft = trigger('fadeOutLeft',
transition('* => void', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.EXITING} ${FuseAnimationCurves.ACCELERATION_CURVE}`
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
}
}
)
@@ -320,7 +320,7 @@ const fadeOutRight = trigger('fadeOutRight',
transition('* => void', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.EXITING} ${FuseAnimationCurves.ACCELERATION_CURVE}`
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
}
}
)

View File

@@ -4,7 +4,7 @@ import { shake } from './shake';
import { slideInBottom, slideInLeft, slideInRight, slideInTop, slideOutBottom, slideOutLeft, slideOutRight, slideOutTop } from './slide';
import { zoomIn, zoomOut } from './zoom';
export const FuseAnimations = [
export const fuseAnimations = [
expandCollapse,
fadeIn, fadeInTop, fadeInBottom, fadeInLeft, fadeInRight,
fadeOut, fadeOutTop, fadeOutBottom, fadeOutLeft, fadeOutRight,

View File

@@ -25,7 +25,7 @@ const slideInTop = trigger('slideInTop',
transition('void => *', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.ENTERING} ${FuseAnimationCurves.DECELERATION_CURVE}`
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
}
}
)
@@ -56,7 +56,7 @@ const slideInBottom = trigger('slideInBottom',
transition('void => *', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.ENTERING} ${FuseAnimationCurves.DECELERATION_CURVE}`
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
}
}
)
@@ -87,7 +87,7 @@ const slideInLeft = trigger('slideInLeft',
transition('void => *', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.ENTERING} ${FuseAnimationCurves.DECELERATION_CURVE}`
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
}
}
)
@@ -118,7 +118,7 @@ const slideInRight = trigger('slideInRight',
transition('void => *', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.ENTERING} ${FuseAnimationCurves.DECELERATION_CURVE}`
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
}
}
)
@@ -149,7 +149,7 @@ const slideOutTop = trigger('slideOutTop',
transition('* => void', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.EXITING} ${FuseAnimationCurves.ACCELERATION_CURVE}`
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
}
}
)
@@ -180,7 +180,7 @@ const slideOutBottom = trigger('slideOutBottom',
transition('* => void', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.EXITING} ${FuseAnimationCurves.ACCELERATION_CURVE}`
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
}
}
)
@@ -211,7 +211,7 @@ const slideOutLeft = trigger('slideOutLeft',
transition('* => void', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.EXITING} ${FuseAnimationCurves.ACCELERATION_CURVE}`
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
}
}
)
@@ -242,7 +242,7 @@ const slideOutRight = trigger('slideOutRight',
transition('* => void', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.EXITING} ${FuseAnimationCurves.ACCELERATION_CURVE}`
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
}
}
)

View File

@@ -28,7 +28,7 @@ const zoomIn = trigger('zoomIn',
transition('void => *', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.ENTERING} ${FuseAnimationCurves.DECELERATION_CURVE}`
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
}
}
)
@@ -62,7 +62,7 @@ const zoomOut = trigger('zoomOut',
transition('* => void', animate('{{timings}}'),
{
params: {
timings: `${FuseAnimationDurations.EXITING} ${FuseAnimationCurves.ACCELERATION_CURVE}`
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
}
}
)

View File

@@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Ho
import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';
import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
import { FuseAnimations } from '@fuse/animations';
import { fuseAnimations } from '@fuse/animations';
import { FuseAlertAppearance, FuseAlertType } from '@fuse/components/alert/alert.types';
import { FuseAlertService } from '@fuse/components/alert/alert.service';
import { FuseUtilsService } from '@fuse/services/utils/utils.service';
@@ -13,14 +13,16 @@ import { FuseUtilsService } from '@fuse/services/utils/utils.service';
styleUrls : ['./alert.component.scss'],
encapsulation : ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
animations : FuseAnimations,
animations : fuseAnimations,
exportAs : 'fuseAlert'
})
export class FuseAlertComponent implements OnChanges, OnInit, OnDestroy
{
/* eslint-disable @typescript-eslint/naming-convention */
static ngAcceptInputType_dismissible: BooleanInput;
static ngAcceptInputType_dismissed: BooleanInput;
static ngAcceptInputType_showIcon: BooleanInput;
/* eslint-enable @typescript-eslint/naming-convention */
@Input() appearance: FuseAlertAppearance = 'soft';
@Input() dismissed: boolean = false;
@@ -115,7 +117,7 @@ export class FuseAlertComponent implements OnChanges, OnInit, OnDestroy
// Subscribe to the dismiss calls
this._fuseAlertService.onDismiss
.pipe(
filter((name) => this.name === name),
filter(name => this.name === name),
takeUntil(this._unsubscribeAll)
)
.subscribe(() => {
@@ -127,7 +129,7 @@ export class FuseAlertComponent implements OnChanges, OnInit, OnDestroy
// Subscribe to the show calls
this._fuseAlertService.onShow
.pipe(
filter((name) => this.name === name),
filter(name => this.name === name),
takeUntil(this._unsubscribeAll)
)
.subscribe(() => {

View File

@@ -1,6 +1,6 @@
import { Component, HostBinding, Input, OnChanges, SimpleChanges, ViewEncapsulation } from '@angular/core';
import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
import { FuseAnimations } from '@fuse/animations';
import { fuseAnimations } from '@fuse/animations';
import { FuseCardFace } from '@fuse/components/card/card.types';
@Component({
@@ -8,13 +8,15 @@ import { FuseCardFace } from '@fuse/components/card/card.types';
templateUrl : './card.component.html',
styleUrls : ['./card.component.scss'],
encapsulation: ViewEncapsulation.None,
animations : FuseAnimations,
animations : fuseAnimations,
exportAs : 'fuseCard'
})
export class FuseCardComponent implements OnChanges
{
/* eslint-disable @typescript-eslint/naming-convention */
static ngAcceptInputType_expanded: BooleanInput;
static ngAcceptInputType_flippable: BooleanInput;
/* eslint-enable @typescript-eslint/naming-convention */
@Input() expanded: boolean = false;
@Input() face: FuseCardFace = 'front';

View File

@@ -23,14 +23,14 @@ import { Moment } from 'moment';
})
export class FuseDateRangeComponent implements ControlValueAccessor, OnInit, OnDestroy
{
@Output() readonly rangeChanged: EventEmitter<{ start: string, end: string }> = new EventEmitter<{ start: string; end: string }>();
@Output() readonly rangeChanged: EventEmitter<{ start: string; end: string }> = new EventEmitter<{ start: string; end: string }>();
@ViewChild('matMonthView1') private _matMonthView1: MatMonthView<any>;
@ViewChild('matMonthView2') private _matMonthView2: MatMonthView<any>;
@ViewChild('pickerPanelOrigin', {read: ElementRef}) private _pickerPanelOrigin: ElementRef;
@ViewChild('pickerPanel') private _pickerPanel: TemplateRef<any>;
@HostBinding('class.fuse-date-range') private _defaultClassNames = true;
activeDates: { month1: Moment | null, month2: Moment | null } = {
activeDates: { month1: Moment | null; month2: Moment | null } = {
month1: null,
month2: null
};
@@ -41,7 +41,7 @@ export class FuseDateRangeComponent implements ControlValueAccessor, OnInit, OnD
private _onChange: (value: any) => void;
private _onTouched: (value: any) => void;
private _programmaticChange!: boolean;
private _range: { start: Moment | null, end: Moment | null } = {
private _range: { start: Moment | null; end: Moment | null } = {
start: null,
end : null
};
@@ -330,7 +330,7 @@ export class FuseDateRangeComponent implements ControlValueAccessor, OnInit, OnD
*
* @param range
*/
writeValue(range: { start: string, end: string }): void
writeValue(range: { start: string; end: string }): void
{
// Set this change as a programmatic one
this._programmaticChange = true;
@@ -481,11 +481,8 @@ export class FuseDateRangeComponent implements ControlValueAccessor, OnInit, OnD
*/
dateFilter(): any
{
return (date: Moment): boolean => {
// If we are selecting the end date, disable all the dates that comes before the start date
return !(this.setWhichDate === 'end' && date.isBefore(this._range.start, 'day'));
};
// If we are selecting the end date, disable all the dates that comes before the start date
return (date: Moment): boolean => !(this.setWhichDate === 'end' && date.isBefore(this._range.start, 'day'));
}
/**
@@ -667,7 +664,7 @@ export class FuseDateRangeComponent implements ControlValueAccessor, OnInit, OnD
private _parseTime(value: string): Moment
{
// Parse the time using the time regexp
const timeArr = value.split(this._timeRegExp).filter((part) => part !== '');
const timeArr = value.split(this._timeRegExp).filter(part => part !== '');
// Get the meridiem
const meridiem = timeArr[2] || null;

View File

@@ -14,9 +14,11 @@ import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
})
export class FuseDrawerComponent implements OnChanges, OnInit, OnDestroy
{
/* eslint-disable @typescript-eslint/naming-convention */
static ngAcceptInputType_fixed: BooleanInput;
static ngAcceptInputType_opened: BooleanInput;
static ngAcceptInputType_transparentOverlay: BooleanInput;
/* eslint-enable @typescript-eslint/naming-convention */
@Input() fixed: boolean = false;
@Input() mode: FuseDrawerMode = 'side';
@@ -76,6 +78,40 @@ export class FuseDrawerComponent implements OnChanges, OnInit, OnDestroy
};
}
// -----------------------------------------------------------------------------------------------------
// @ Decorated methods
// -----------------------------------------------------------------------------------------------------
/**
* On mouseenter
*
* @private
*/
@HostListener('mouseenter')
private _onMouseenter(): void
{
// Enable the animations
this._enableAnimations();
// Set the hovered
this._hovered = true;
}
/**
* On mouseleave
*
* @private
*/
@HostListener('mouseleave')
private _onMouseleave(): void
{
// Enable the animations
this._enableAnimations();
// Set the hovered
this._hovered = false;
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
@@ -347,36 +383,6 @@ export class FuseDrawerComponent implements OnChanges, OnInit, OnDestroy
});
}
/**
* On mouseenter
*
* @private
*/
@HostListener('mouseenter')
private _onMouseenter(): void
{
// Enable the animations
this._enableAnimations();
// Set the hovered
this._hovered = true;
}
/**
* On mouseleave
*
* @private
*/
@HostListener('mouseleave')
private _onMouseleave(): void
{
// Enable the animations
this._enableAnimations();
// Set the hovered
this._hovered = false;
}
/**
* Open/close the drawer
*

View File

@@ -0,0 +1,7 @@
<!-- Button -->
<button
mat-icon-button
[matTooltip]="'Toggle Fullscreen'"
(click)="toggleFullscreen()">
<mat-icon [svgIcon]="'heroicons_outline:arrows-expand'"></mat-icon>
</button>

View File

@@ -0,0 +1,164 @@
import { ChangeDetectionStrategy, Component, Inject, OnInit, ViewEncapsulation } from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { FSDocument, FSDocumentElement } from '@fuse/components/fullscreen/fullscreen.types';
@Component({
selector : 'fuse-fullscreen',
templateUrl : './fullscreen.component.html',
encapsulation : ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
exportAs : 'fuseFullscreen'
})
export class FuseFullscreenComponent implements OnInit
{
private _fsDoc: FSDocument;
private _fsDocEl: FSDocumentElement;
private _isFullscreen: boolean = false;
/**
* Constructor
*/
constructor(@Inject(DOCUMENT) private _document: Document)
{
this._fsDoc = _document as FSDocument;
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
this._fsDocEl = document.documentElement as FSDocumentElement;
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Toggle the fullscreen mode
*/
toggleFullscreen(): void
{
// Check if the fullscreen is open
this._isFullscreen = this._getBrowserFullscreenElement() !== null;
// Toggle the fullscreen
if ( this._isFullscreen )
{
this._closeFullscreen();
}
else
{
this._openFullscreen();
}
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Get browser's fullscreen element
*
* @private
*/
private _getBrowserFullscreenElement(): Element
{
if ( typeof this._fsDoc.fullscreenElement !== 'undefined' )
{
return this._fsDoc.fullscreenElement;
}
if ( typeof this._fsDoc.mozFullScreenElement !== 'undefined' )
{
return this._fsDoc.mozFullScreenElement;
}
if ( typeof this._fsDoc.msFullscreenElement !== 'undefined' )
{
return this._fsDoc.msFullscreenElement;
}
if ( typeof this._fsDoc.webkitFullscreenElement !== 'undefined' )
{
return this._fsDoc.webkitFullscreenElement;
}
throw new Error('Fullscreen mode is not supported by this browser');
}
/**
* Open the fullscreen
*
* @private
*/
private _openFullscreen(): void
{
if ( this._fsDocEl.requestFullscreen )
{
this._fsDocEl.requestFullscreen();
return;
}
// Firefox
if ( this._fsDocEl.mozRequestFullScreen )
{
this._fsDocEl.mozRequestFullScreen();
return;
}
// Chrome, Safari and Opera
if ( this._fsDocEl.webkitRequestFullscreen )
{
this._fsDocEl.webkitRequestFullscreen();
return;
}
// IE/Edge
if ( this._fsDocEl.msRequestFullscreen )
{
this._fsDocEl.msRequestFullscreen();
return;
}
}
/**
* Close the fullscreen
*
* @private
*/
private _closeFullscreen(): void
{
if ( this._fsDoc.exitFullscreen )
{
this._fsDoc.exitFullscreen();
return;
}
// Firefox
if ( this._fsDoc.mozCancelFullScreen )
{
this._fsDoc.mozCancelFullScreen();
return;
}
// Chrome, Safari and Opera
if ( this._fsDoc.webkitExitFullscreen )
{
this._fsDoc.webkitExitFullscreen();
return;
}
// IE/Edge
else if ( this._fsDoc.msExitFullscreen )
{
this._fsDoc.msExitFullscreen();
return;
}
}
}

View File

@@ -0,0 +1,22 @@
import { NgModule } from '@angular/core';
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';
@NgModule({
declarations: [
FuseFullscreenComponent
],
imports : [
MatButtonModule,
MatIconModule,
MatTooltipModule
],
exports : [
FuseFullscreenComponent
]
})
export class FuseFullscreenModule
{
}

View File

@@ -0,0 +1,16 @@
export interface FSDocument extends HTMLDocument
{
mozFullScreenElement?: Element;
mozCancelFullScreen?: () => void;
msFullscreenElement?: Element;
msExitFullscreen?: () => void;
webkitFullscreenElement?: Element;
webkitExitFullscreen?: () => void;
}
export interface FSDocumentElement extends HTMLElement
{
mozRequestFullScreen?: () => void;
msRequestFullscreen?: () => void;
webkitRequestFullscreen?: () => void;
}

View File

@@ -0,0 +1 @@
export * from '@fuse/components/fullscreen/public-api';

View File

@@ -0,0 +1,3 @@
export * from '@fuse/components/fullscreen/fullscreen.component';
export * from '@fuse/components/fullscreen/fullscreen.module';
export * from '@fuse/components/fullscreen/fullscreen.types';

View File

@@ -60,7 +60,7 @@ export class FuseHighlightService
}
// Iterate through the lines
lines.filter((line) => line.length)
lines.filter(line => line.length)
.forEach((line, index) => {
// Always get the indentation of the first line so we can
@@ -77,6 +77,6 @@ export class FuseHighlightService
// Iterate through the lines one more time, remove the extra
// indentation, join them together and return it
return lines.map((line) => line.substring(indentation)).join('\n');
return lines.map(line => line.substring(indentation)).join('\n');
}
}

View File

@@ -0,0 +1 @@
export * from '@fuse/components/card/public-api';

View File

@@ -0,0 +1,3 @@
<div class="flex">
<ng-container *ngTemplateOutlet="columnsTemplate; context: { $implicit: distributedColumns }"></ng-container>
</div>

View File

@@ -0,0 +1,87 @@
import { AfterViewInit, Component, Input, OnChanges, SimpleChanges, TemplateRef, ViewEncapsulation } from '@angular/core';
import { fuseAnimations } from '@fuse/animations';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
@Component({
selector : 'fuse-masonry',
templateUrl : './masonry.component.html',
styleUrls : ['./masonry.component.scss'],
encapsulation: ViewEncapsulation.None,
animations : fuseAnimations,
exportAs : 'fuseMasonry'
})
export class FuseMasonryComponent implements OnChanges, AfterViewInit
{
@Input() columnsTemplate: TemplateRef<any>;
@Input() columns: number;
@Input() items: any[] = [];
distributedColumns: any[] = [];
/**
* Constructor
*/
constructor(private _fuseMediaWatcherService: FuseMediaWatcherService)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On changes
*
* @param changes
*/
ngOnChanges(changes: SimpleChanges): void
{
// Columns
if ( 'columns' in changes )
{
// Distribute the items
this._distributeItems();
}
// Items
if ( 'items' in changes )
{
// Distribute the items
this._distributeItems();
}
}
/**
* After view init
*/
ngAfterViewInit(): void
{
// Distribute the items for the first time
this._distributeItems();
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Distribute items into columns
*/
private _distributeItems(): void
{
// Return an empty array if there are no items
if ( this.items.length === 0 )
{
this.distributedColumns = [];
return;
}
// Prepare the distributed columns array
this.distributedColumns = Array.from(Array(this.columns), item => ({items: []}));
// Distribute the items to columns
for ( let i = 0; i < this.items.length; i++ )
{
this.distributedColumns[i % this.columns].items.push(this.items[i]);
}
}
}

View File

@@ -0,0 +1,18 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FuseMasonryComponent } from '@fuse/components/masonry/masonry.component';
@NgModule({
declarations: [
FuseMasonryComponent
],
imports : [
CommonModule
],
exports : [
FuseMasonryComponent
]
})
export class FuseMasonryModule
{
}

View File

@@ -0,0 +1,2 @@
export * from '@fuse/components/masonry/masonry.component';
export * from '@fuse/components/masonry/masonry.module';

View File

@@ -15,7 +15,9 @@ import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types
})
export class FuseHorizontalNavigationBranchItemComponent implements OnInit, OnDestroy
{
/* eslint-disable @typescript-eslint/naming-convention */
static ngAcceptInputType_child: BooleanInput;
/* eslint-enable @typescript-eslint/naming-convention */
@Input() child: boolean = false;
@Input() item: FuseNavigationItem;

View File

@@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewEncapsulation } from '@angular/core';
import { ReplaySubject, Subject } from 'rxjs';
import { FuseAnimations } from '@fuse/animations';
import { fuseAnimations } from '@fuse/animations';
import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types';
import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
import { FuseUtilsService } from '@fuse/services/utils/utils.service';
@@ -9,7 +9,7 @@ import { FuseUtilsService } from '@fuse/services/utils/utils.service';
selector : 'fuse-horizontal-navigation',
templateUrl : './horizontal.component.html',
styleUrls : ['./horizontal.component.scss'],
animations : FuseAnimations,
animations : fuseAnimations,
encapsulation : ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
exportAs : 'fuseHorizontalNavigation'

View File

@@ -67,7 +67,6 @@ export class FuseNavigationService
* Get navigation from storage by key
*
* @param key
* @returns {any}
*/
getNavigation(key: string): FuseNavigationItem[]
{
@@ -97,7 +96,6 @@ export class FuseNavigationService
*
* @param navigation
* @param flatNavigation
* @returns {FuseNavigationItem[]}
*/
getFlatNavigation(navigation: FuseNavigationItem[], flatNavigation: FuseNavigationItem[] = []): FuseNavigationItem[]
{

View File

@@ -15,8 +15,10 @@ import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types
})
export class FuseVerticalNavigationAsideItemComponent implements OnChanges, OnInit, OnDestroy
{
/* eslint-disable @typescript-eslint/naming-convention */
static ngAcceptInputType_autoCollapse: BooleanInput;
static ngAcceptInputType_skipChildren: BooleanInput;
/* eslint-enable @typescript-eslint/naming-convention */
@Input() activeItemId: string;
@Input() autoCollapse: boolean;
@@ -101,6 +103,21 @@ export class FuseVerticalNavigationAsideItemComponent implements OnChanges, OnIn
this._unsubscribeAll.complete();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Track by function for ngFor loops
*
* @param index
* @param item
*/
trackByFn(index: number, item: any): any
{
return item.id || index;
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
@@ -168,19 +185,4 @@ export class FuseVerticalNavigationAsideItemComponent implements OnChanges, OnIn
// Mark for check
this._changeDetectorRef.markForCheck();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Track by function for ngFor loops
*
* @param index
* @param item
*/
trackByFn(index: number, item: any): any
{
return item.id || index;
}
}

View File

@@ -3,7 +3,7 @@ import { NavigationEnd, Router } from '@angular/router';
import { BooleanInput } from '@angular/cdk/coercion';
import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';
import { FuseAnimations } from '@fuse/animations';
import { fuseAnimations } from '@fuse/animations';
import { FuseVerticalNavigationComponent } from '@fuse/components/navigation/vertical/vertical.component';
import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types';
@@ -12,12 +12,14 @@ import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types
selector : 'fuse-vertical-navigation-collapsable-item',
templateUrl : './collapsable.component.html',
styles : [],
animations : FuseAnimations,
animations : fuseAnimations,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class FuseVerticalNavigationCollapsableItemComponent implements OnInit, OnDestroy
{
/* eslint-disable @typescript-eslint/naming-convention */
static ngAcceptInputType_autoCollapse: BooleanInput;
/* eslint-enable @typescript-eslint/naming-convention */
@Input() autoCollapse: boolean;
@Input() item: FuseNavigationItem;
@@ -179,84 +181,6 @@ export class FuseVerticalNavigationCollapsableItemComponent implements OnInit, O
this._unsubscribeAll.complete();
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Check if the given item has the given url
* in one of its children
*
* @param item
* @param currentUrl
* @private
*/
private _hasActiveChild(item: FuseNavigationItem, currentUrl: string): boolean
{
const children = item.children;
if ( !children )
{
return false;
}
for ( const child of children )
{
if ( child.children )
{
if ( this._hasActiveChild(child, currentUrl) )
{
return true;
}
}
// Check if the child has a link and is active
if ( child.link && this._router.isActive(child.link, child.exactMatch || false) )
{
return true;
}
}
return false;
}
/**
* Check if this is a children
* of the given item
*
* @param parent
* @param item
* @return {boolean}
* @private
*/
private _isChildrenOf(parent: FuseNavigationItem, item: FuseNavigationItem): boolean
{
const children = parent.children;
if ( !children )
{
return false;
}
if ( children.indexOf(item) > -1 )
{
return true;
}
for ( const child of children )
{
if ( child.children )
{
if ( this._isChildrenOf(child, item) )
{
return true;
}
}
}
return false;
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
@@ -343,4 +267,81 @@ export class FuseVerticalNavigationCollapsableItemComponent implements OnInit, O
{
return item.id || index;
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Check if the given item has the given url
* in one of its children
*
* @param item
* @param currentUrl
* @private
*/
private _hasActiveChild(item: FuseNavigationItem, currentUrl: string): boolean
{
const children = item.children;
if ( !children )
{
return false;
}
for ( const child of children )
{
if ( child.children )
{
if ( this._hasActiveChild(child, currentUrl) )
{
return true;
}
}
// Check if the child has a link and is active
if ( child.link && this._router.isActive(child.link, child.exactMatch || false) )
{
return true;
}
}
return false;
}
/**
* Check if this is a children
* of the given item
*
* @param parent
* @param item
* @private
*/
private _isChildrenOf(parent: FuseNavigationItem, item: FuseNavigationItem): boolean
{
const children = parent.children;
if ( !children )
{
return false;
}
if ( children.indexOf(item) > -1 )
{
return true;
}
for ( const child of children )
{
if ( child.children )
{
if ( this._isChildrenOf(child, item) )
{
return true;
}
}
}
return false;
}
}

View File

@@ -14,7 +14,9 @@ import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types
})
export class FuseVerticalNavigationGroupItemComponent implements OnInit, OnDestroy
{
/* eslint-disable @typescript-eslint/naming-convention */
static ngAcceptInputType_autoCollapse: BooleanInput;
/* eslint-enable @typescript-eslint/naming-convention */
@Input() autoCollapse: boolean;
@Input() item: FuseNavigationItem;

View File

@@ -4,7 +4,7 @@ import { NavigationEnd, Router } from '@angular/router';
import { ScrollStrategy, ScrollStrategyOptions } from '@angular/cdk/overlay';
import { merge, ReplaySubject, Subject, Subscription } from 'rxjs';
import { delay, filter, takeUntil } from 'rxjs/operators';
import { FuseAnimations } from '@fuse/animations';
import { fuseAnimations } from '@fuse/animations';
import { FuseNavigationItem, FuseVerticalNavigationAppearance, FuseVerticalNavigationMode, FuseVerticalNavigationPosition } from '@fuse/components/navigation/navigation.types';
import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
import { FuseScrollbarDirective } from '@fuse/directives/scrollbar/scrollbar.directive';
@@ -15,16 +15,18 @@ import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
selector : 'fuse-vertical-navigation',
templateUrl : './vertical.component.html',
styleUrls : ['./vertical.component.scss'],
animations : FuseAnimations,
animations : fuseAnimations,
encapsulation : ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
exportAs : 'fuseVerticalNavigation'
})
export class FuseVerticalNavigationComponent implements OnChanges, OnInit, AfterViewInit, OnDestroy
{
/* eslint-disable @typescript-eslint/naming-convention */
static ngAcceptInputType_inner: BooleanInput;
static ngAcceptInputType_opened: BooleanInput;
static ngAcceptInputType_transparentOverlay: BooleanInput;
/* eslint-enable @typescript-eslint/naming-convention */
@Input() appearance: FuseVerticalNavigationAppearance = 'default';
@Input() autoCollapse: boolean = true;
@@ -151,6 +153,40 @@ export class FuseVerticalNavigationComponent implements OnChanges, OnInit, After
});
}
// -----------------------------------------------------------------------------------------------------
// @ Decorated methods
// -----------------------------------------------------------------------------------------------------
/**
* On mouseenter
*
* @private
*/
@HostListener('mouseenter')
private _onMouseenter(): void
{
// Enable the animations
this._enableAnimations();
// Set the hovered
this._hovered = true;
}
/**
* On mouseleave
*
* @private
*/
@HostListener('mouseleave')
private _onMouseleave(): void
{
// Enable the animations
this._enableAnimations();
// Set the hovered
this._hovered = false;
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
@@ -672,36 +708,6 @@ export class FuseVerticalNavigationComponent implements OnChanges, OnInit, After
});
}
/**
* On mouseenter
*
* @private
*/
@HostListener('mouseenter')
private _onMouseenter(): void
{
// Enable the animations
this._enableAnimations();
// Set the hovered
this._hovered = true;
}
/**
* On mouseleave
*
* @private
*/
@HostListener('mouseleave')
private _onMouseleave(): void
{
// Enable the animations
this._enableAnimations();
// Set the hovered
this._hovered = false;
}
/**
* Open/close the navigation
*

View File

@@ -7,7 +7,7 @@ import { Subject } from 'rxjs';
})
export class FuseAutogrowDirective implements OnChanges, OnInit, OnDestroy
{
// tslint:disable-next-line:no-input-rename
// eslint-disable-next-line @angular-eslint/no-input-rename
@Input('fuseAutogrowVerticalPadding') padding: number = 8;
@HostBinding('rows') private _rows: number = 1;
@@ -41,6 +41,39 @@ export class FuseAutogrowDirective implements OnChanges, OnInit, OnDestroy
};
}
// -----------------------------------------------------------------------------------------------------
// @ 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
// -----------------------------------------------------------------------------------------------------
@@ -78,37 +111,4 @@ export class FuseAutogrowDirective implements OnChanges, OnInit, OnDestroy
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
// -----------------------------------------------------------------------------------------------------
// @ Private 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();
});
});
}
}

View File

@@ -17,7 +17,9 @@ import { ScrollbarGeometry, ScrollbarPosition } from '@fuse/directives/scrollbar
})
export class FuseScrollbarDirective implements OnChanges, OnInit, OnDestroy
{
/* eslint-disable @typescript-eslint/naming-convention */
static ngAcceptInputType_fuseScrollbar: BooleanInput;
/* eslint-enable @typescript-eslint/naming-convention */
@Input() fuseScrollbar: boolean = true;
@Input() fuseScrollbarOptions: PerfectScrollbar.Options;
@@ -246,8 +248,8 @@ export class FuseScrollbarDirective implements OnChanges, OnInit, OnDestroy
/**
* Scroll to X
*
* @param {number} x
* @param {number} speed
* @param x
* @param speed
*/
scrollToX(x: number, speed?: number): void
{
@@ -257,8 +259,8 @@ export class FuseScrollbarDirective implements OnChanges, OnInit, OnDestroy
/**
* Scroll to Y
*
* @param {number} y
* @param {number} speed
* @param y
* @param speed
*/
scrollToY(y: number, speed?: number): void
{
@@ -268,8 +270,8 @@ export class FuseScrollbarDirective implements OnChanges, OnInit, OnDestroy
/**
* Scroll to top
*
* @param {number} offset
* @param {number} speed
* @param offset
* @param speed
*/
scrollToTop(offset: number = 0, speed?: number): void
{
@@ -279,8 +281,8 @@ export class FuseScrollbarDirective implements OnChanges, OnInit, OnDestroy
/**
* Scroll to bottom
*
* @param {number} offset
* @param {number} speed
* @param offset
* @param speed
*/
scrollToBottom(offset: number = 0, speed?: number): void
{
@@ -291,8 +293,8 @@ export class FuseScrollbarDirective implements OnChanges, OnInit, OnDestroy
/**
* Scroll to left
*
* @param {number} offset
* @param {number} speed
* @param offset
* @param speed
*/
scrollToLeft(offset: number = 0, speed?: number): void
{
@@ -302,8 +304,8 @@ export class FuseScrollbarDirective implements OnChanges, OnInit, OnDestroy
/**
* Scroll to right
*
* @param {number} offset
* @param {number} speed
* @param offset
* @param speed
*/
scrollToRight(offset: number = 0, speed?: number): void
{
@@ -314,10 +316,10 @@ export class FuseScrollbarDirective implements OnChanges, OnInit, OnDestroy
/**
* Scroll to element
*
* @param {string} qs
* @param {number} offset
* @param {boolean} ignoreVisible If true, scrollToElement won't happen if element is already inside the current viewport
* @param {number} speed
* @param qs
* @param offset
* @param ignoreVisible If true, scrollToElement won't happen if element is already inside the current viewport
* @param speed
*/
scrollToElement(qs: string, offset: number = 0, ignoreVisible: boolean = false, speed?: number): void
{

View File

@@ -9,11 +9,11 @@ import { FuseMockApiMethods } from '@fuse/lib/mock-api/mock-api.types';
export class FuseMockApiService
{
private _handlers: { [key: string]: Map<string, FuseMockApiHandler> } = {
DELETE: new Map<string, FuseMockApiHandler>(),
GET : new Map<string, FuseMockApiHandler>(),
PATCH : new Map<string, FuseMockApiHandler>(),
POST : new Map<string, FuseMockApiHandler>(),
PUT : new Map<string, FuseMockApiHandler>()
'delete': new Map<string, FuseMockApiHandler>(),
'get' : new Map<string, FuseMockApiHandler>(),
'patch' : new Map<string, FuseMockApiHandler>(),
'post' : new Map<string, FuseMockApiHandler>(),
'put' : new Map<string, FuseMockApiHandler>()
};
/**
@@ -34,10 +34,10 @@ export class FuseMockApiService
* @param method
* @param url
*/
findHandler(method: string, url: string): { handler: FuseMockApiHandler | undefined, urlParams: { [key: string]: string } }
findHandler(method: string, url: string): { handler: FuseMockApiHandler | undefined; urlParams: { [key: string]: string } }
{
// Prepare the return object
const matchingHandler: { handler: FuseMockApiHandler | undefined, urlParams: { [key: string]: string } } = {
const matchingHandler: { handler: FuseMockApiHandler | undefined; urlParams: { [key: string]: string } } = {
handler : undefined,
urlParams: {}
};
@@ -46,7 +46,7 @@ export class FuseMockApiService
const urlParts = url.split('/');
// Get all related request handlers
const handlers = this._handlers[method.toUpperCase()];
const handlers = this._handlers[method.toLowerCase()];
// Iterate through the handlers
handlers.forEach((handler, handlerUrl) => {
@@ -93,7 +93,7 @@ export class FuseMockApiService
*/
onDelete(url: string, delay?: number): FuseMockApiHandler
{
return this._registerHandler('DELETE', url, delay);
return this._registerHandler('delete', url, delay);
}
/**
@@ -104,7 +104,7 @@ export class FuseMockApiService
*/
onGet(url: string, delay?: number): FuseMockApiHandler
{
return this._registerHandler('GET', url, delay);
return this._registerHandler('get', url, delay);
}
/**
@@ -115,7 +115,7 @@ export class FuseMockApiService
*/
onPatch(url: string, delay?: number): FuseMockApiHandler
{
return this._registerHandler('PATCH', url, delay);
return this._registerHandler('patch', url, delay);
}
/**
@@ -126,7 +126,7 @@ export class FuseMockApiService
*/
onPost(url: string, delay?: number): FuseMockApiHandler
{
return this._registerHandler('POST', url, delay);
return this._registerHandler('post', url, delay);
}
/**
@@ -137,7 +137,7 @@ export class FuseMockApiService
*/
onPut(url: string, delay?: number): FuseMockApiHandler
{
return this._registerHandler('PUT', url, delay);
return this._registerHandler('put', url, delay);
}
// -----------------------------------------------------------------------------------------------------

View File

@@ -6,8 +6,8 @@ export type FuseMockApiReplyCallback =
| undefined;
export type FuseMockApiMethods =
| 'GET'
| 'POST'
| 'PUT'
| 'PATCH'
| 'DELETE';
| 'get'
| 'post'
| 'put'
| 'patch'
| 'delete';

View File

@@ -16,7 +16,7 @@ export class FuseMockApiUtils
*/
static guid(): string
{
/* tslint:disable */
/* eslint-disable */
let d = new Date().getTime();
@@ -32,6 +32,6 @@ export class FuseMockApiUtils
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
/* tslint:enable */
/* eslint-enable */
}
}

View File

@@ -28,9 +28,7 @@ export class FuseFindByKeyPipe implements PipeTransform
// If the given value is an array of strings...
if ( Array.isArray(value) )
{
return value.map((item) => {
return source.find((sourceItem) => sourceItem[key] === item);
});
return value.map(item => source.find(sourceItem => sourceItem[key] === item));
}
// If the value is a string...

View File

@@ -7,7 +7,7 @@ import { FuseTailwindService } from '@fuse/services/tailwind/tailwind.service';
@Injectable()
export class FuseMediaWatcherService
{
private _onMediaChange: ReplaySubject<{ matchingAliases: string[], matchingQueries: any }> = new ReplaySubject<{ matchingAliases: string[], matchingQueries: any }>(1);
private _onMediaChange: ReplaySubject<{ matchingAliases: string[]; matchingQueries: any }> = new ReplaySubject<{ matchingAliases: string[]; matchingQueries: any }>(1);
/**
* Constructor
@@ -18,7 +18,7 @@ export class FuseMediaWatcherService
)
{
this._fuseTailwindConfigService.tailwindConfig$.pipe(
switchMap((config) => this._breakpointObserver.observe(Object.values(config.breakpoints)).pipe(
switchMap(config => this._breakpointObserver.observe(Object.values(config.breakpoints)).pipe(
map((state) => {
// Prepare the observable values and set their defaults
@@ -57,7 +57,7 @@ export class FuseMediaWatcherService
/**
* Getter for _onMediaChange
*/
get onMediaChange$(): Observable<{ matchingAliases: string[], matchingQueries: any }>
get onMediaChange$(): Observable<{ matchingAliases: string[]; matchingQueries: any }>
{
return this._onMediaChange.asObservable();
}

View File

@@ -36,7 +36,6 @@
.mat-tab-body-content {
.fuse-highlight {
margin: -24px;
pre {
margin: 0;

View File

@@ -1,23 +1,20 @@
@use '~@angular/material' as mat;
@use "sass:map";
@import '~@angular/material/theming';
/** Include the core Angular Material styles */
@include mat-core();
@include mat.core();
/** Configure the Angular Material typography */
@include angular-material-typography(
mat-typography-config(
@include mat.all-component-typographies(
mat.define-typography-config(
$font-family: theme('fontFamily.sans'),
$title: mat-typography-level(1.25rem, 2rem, 600),
$body-2: mat-typography-level(0.875rem, 1.5rem, 600),
$button: mat-typography-level(0.875rem, 0.875rem, 500),
$input: mat-typography-level(0.875rem, 1.2857142857, 400) // line-height: 20px
$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
)
);
/** Configure the Angular Material density **/
@include angular-material-density(-2);
/** Prepare the Background and Foreground maps */
$background-light: (
status-bar: #CBD5E1, /* blueGray.300 */
@@ -135,7 +132,7 @@ $palettes: ();
CSS Custom Properties to dynamically switch the colors. */
body.light,
body .light {
$base-light-theme: mat-light-theme((
$base-light-theme: mat.define-light-theme((
color: ($palettes)
));
@@ -148,22 +145,15 @@ body .light {
foreground: $foreground-light,
background: $background-light
),
typography: null,
density: null,
primary: map.get(map.get($base-light-theme, color), primary),
accent: map.get(map.get($base-light-theme, color), accent),
warn: map.get(map.get($base-light-theme, color), warn),
is-dark: map.get(map.get($base-light-theme, color), is-dark),
foreground: $foreground-light,
background: $background-light
density: -2
);
@include angular-material-theme($light-theme);
@include mat.all-component-themes($light-theme);
}
body.dark,
body .dark {
$base-dark-theme: mat-dark-theme((
$base-dark-theme: mat.define-dark-theme((
color: ($palettes)
));
@@ -176,15 +166,8 @@ body .dark {
foreground: $foreground-dark,
background: $background-dark
),
typography: null,
density: null,
primary: map.get(map.get($base-dark-theme, color), primary),
accent: map.get(map.get($base-dark-theme, color), accent),
warn: map.get(map.get($base-dark-theme, color), warn),
is-dark: map.get(map.get($base-dark-theme, color), is-dark),
foreground: $foreground-dark,
background: $background-dark
density: -2
);
@include angular-material-theme($dark-theme);
@include mat.all-component-colors($dark-theme);
}

View File

@@ -72,13 +72,17 @@ function generateThemesObject(themes)
return _.map(_.cloneDeep(themes), (value, key) =>
{
const theme = normalizeTheme(value);
const primary = (theme && theme.primary && theme.primary.DEFAULT) ? theme.primary.DEFAULT : normalizedDefaultTheme.primary.DEFAULT;
const accent = (theme && theme.accent && theme.accent.DEFAULT) ? theme.accent.DEFAULT : normalizedDefaultTheme.accent.DEFAULT;
const warn = (theme && theme.warn && theme.warn.DEFAULT) ? theme.warn.DEFAULT : normalizedDefaultTheme.warn.DEFAULT;
return _.fromPairs([
[
key,
{
primary: theme?.primary?.DEFAULT ?? normalizedDefaultTheme.primary.DEFAULT,
accent : theme?.accent?.DEFAULT ?? normalizedDefaultTheme.accent.DEFAULT,
warn : theme?.warn?.DEFAULT ?? normalizedDefaultTheme.warn.DEFAULT
primary,
accent,
warn
}
]
]);

View File

@@ -56,7 +56,7 @@ const utilities = plugin(({
}
},
{
variants: ['dark', 'responsive']
variants: ['dark', 'responsive', 'group-hover', 'hover']
}
);

View File

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

1
src/_redirects Normal file
View File

@@ -0,0 +1 @@
/* /index.html 200

View File

@@ -15,8 +15,7 @@ import { appRoutes } from 'app/app.routing';
const routerConfig: ExtraOptions = {
scrollPositionRestoration: 'enabled',
preloadingStrategy : PreloadAllModules,
relativeLinkResolution : 'legacy'
preloadingStrategy : PreloadAllModules
};
@NgModule({

View File

@@ -5,7 +5,7 @@ import { LayoutComponent } from 'app/layout/layout.component';
import { InitialDataResolver } from 'app/app.resolvers';
// @formatter:off
// tslint:disable:max-line-length
/* eslint-disable max-len */
export const appRoutes: Route[] = [
// Redirect empty path to '/dashboards/project'
@@ -82,12 +82,15 @@ export const appRoutes: Route[] = [
// Apps
{path: 'apps', children: [
{path: 'academy', loadChildren: () => import('app/modules/admin/apps/academy/academy.module').then(m => m.AcademyModule)},
{path: 'calendar', loadChildren: () => import('app/modules/admin/apps/calendar/calendar.module').then(m => m.CalendarModule)},
{path: 'chat', loadChildren: () => import('app/modules/admin/apps/chat/chat.module').then(m => m.ChatModule)},
{path: 'contacts', loadChildren: () => import('app/modules/admin/apps/contacts/contacts.module').then(m => m.ContactsModule)},
{path: 'ecommerce', loadChildren: () => import('app/modules/admin/apps/ecommerce/ecommerce.module').then(m => m.ECommerceModule)},
{path: 'file-manager', loadChildren: () => import('app/modules/admin/apps/file-manager/file-manager.module').then(m => m.FileManagerModule)},
{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: 'tasks', loadChildren: () => import('app/modules/admin/apps/tasks/tasks.module').then(m => m.TasksModule)},
]},
@@ -127,6 +130,9 @@ export const appRoutes: Route[] = [
// Profile
{path: 'profile', loadChildren: () => import('app/modules/admin/pages/profile/profile.module').then(m => m.ProfileModule)},
// Settings
{path: 'settings', loadChildren: () => import('app/modules/admin/pages/settings/settings.module').then(m => m.SettingsModule)},
]},
// User interface

View File

@@ -8,10 +8,10 @@ export interface InitialData
{
messages: Message[];
navigation: {
compact: FuseNavigationItem[],
default: FuseNavigationItem[],
futuristic: FuseNavigationItem[],
horizontal: FuseNavigationItem[]
compact: FuseNavigationItem[];
default: FuseNavigationItem[];
futuristic: FuseNavigationItem[];
horizontal: FuseNavigationItem[];
};
notifications: Notification[];
shortcuts: Shortcut[];

View File

@@ -29,12 +29,12 @@ export class AuthService
*/
set accessToken(token: string)
{
localStorage.setItem('access_token', token);
localStorage.setItem('accessToken', token);
}
get accessToken(): string
{
return localStorage.getItem('access_token') ?? '';
return localStorage.getItem('accessToken') ?? '';
}
// -----------------------------------------------------------------------------------------------------
@@ -66,7 +66,7 @@ export class AuthService
*
* @param credentials
*/
signIn(credentials: { email: string, password: string }): Observable<any>
signIn(credentials: { email: string; password: string }): Observable<any>
{
// Throw error, if the user is already logged in
if ( this._authenticated )
@@ -78,7 +78,7 @@ export class AuthService
switchMap((response: any) => {
// Store the access token in the local storage
this.accessToken = response.access_token;
this.accessToken = response.accessToken;
// Set the authenticated flag to true
this._authenticated = true;
@@ -99,17 +99,17 @@ export class AuthService
{
// Renew token
return this._httpClient.post('api/auth/refresh-access-token', {
access_token: this.accessToken
accessToken: this.accessToken
}).pipe(
catchError(() => {
catchError(() =>
// Return false
return of(false);
}),
of(false)
),
switchMap((response: any) => {
// Store the access token in the local storage
this.accessToken = response.access_token;
this.accessToken = response.accessToken;
// Set the authenticated flag to true
this._authenticated = true;
@@ -129,7 +129,7 @@ export class AuthService
signOut(): Observable<any>
{
// Remove the access token from the local storage
localStorage.removeItem('access_token');
localStorage.removeItem('accessToken');
// Set the authenticated flag to false
this._authenticated = false;
@@ -143,7 +143,7 @@ export class AuthService
*
* @param user
*/
signUp(user: { name: string, email: string, password: string, company: string }): Observable<any>
signUp(user: { name: string; email: string; password: string; company: string }): Observable<any>
{
return this._httpClient.post('api/auth/sign-up', user);
}
@@ -153,7 +153,7 @@ export class AuthService
*
* @param credentials
*/
unlockSession(credentials: { email: string, password: string }): Observable<any>
unlockSession(credentials: { email: string; password: string }): Observable<any>
{
return this._httpClient.post('api/auth/unlock-session', credentials);
}

View File

@@ -14,6 +14,38 @@ export class AuthUtils
{
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Is token expired?
*
* @param token
* @param offsetSeconds
*/
static isTokenExpired(token: string, offsetSeconds?: number): boolean
{
// Return if there is no token
if ( !token || token === '' )
{
return true;
}
// Get the expiration date
const date = this._getTokenExpirationDate(token);
offsetSeconds = offsetSeconds || 0;
if ( date === null )
{
return true;
}
// Check if the token is expired
return !(date.valueOf() > new Date().valueOf() + offsetSeconds * 1000);
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
@@ -39,7 +71,7 @@ export class AuthUtils
);
}
/* tslint:disable */
/* eslint-disable */
for (
// initialize result and counters
let bc = 0, bs: any, buffer: any, idx = 0;
@@ -60,7 +92,7 @@ export class AuthUtils
// try to find character in table (0-63, not found => -1)
buffer = chars.indexOf(buffer);
}
/* tslint:enable */
/* eslint-enable */
return output;
}
@@ -75,9 +107,7 @@ export class AuthUtils
{
return decodeURIComponent(
Array.prototype.map
.call(this._b64decode(str), (c: any) => {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
})
.call(this._b64decode(str), (c: any) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
.join('')
);
}
@@ -171,36 +201,4 @@ export class AuthUtils
return date;
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Is token expired?
*
* @param token
* @param offsetSeconds
*/
static isTokenExpired(token: string, offsetSeconds?: number): boolean
{
// Return if there is no token
if ( !token || token === '' )
{
return true;
}
// Get the expiration date
const date = this._getTokenExpirationDate(token);
offsetSeconds = offsetSeconds || 0;
if ( date === null )
{
return true;
}
// Check if the token is expired
return !(date.valueOf() > new Date().valueOf() + offsetSeconds * 1000);
}
}

View File

@@ -19,39 +19,6 @@ export class AuthGuard implements CanActivate, CanActivateChild, CanLoad
{
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Check the authenticated status
*
* @param redirectURL
* @private
*/
private _check(redirectURL: string): Observable<boolean>
{
// Check the authentication status
return this._authService.check()
.pipe(
switchMap((authenticated) => {
// If the user is not authenticated...
if ( !authenticated )
{
// Redirect to the sign-in page
this._router.navigate(['sign-in'], {queryParams: {redirectURL}});
// Prevent the access
return of(false);
}
// Allow the access
return of(true);
})
);
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
@@ -90,4 +57,37 @@ export class AuthGuard implements CanActivate, CanActivateChild, CanLoad
{
return this._check('/');
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Check the authenticated status
*
* @param redirectURL
* @private
*/
private _check(redirectURL: string): Observable<boolean>
{
// Check the authentication status
return this._authService.check()
.pipe(
switchMap((authenticated) => {
// If the user is not authenticated...
if ( !authenticated )
{
// Redirect to the sign-in page
this._router.navigate(['sign-in'], {queryParams: {redirectURL}});
// Prevent the access
return of(false);
}
// Allow the access
return of(true);
})
);
}
}

View File

@@ -19,38 +19,6 @@ export class NoAuthGuard implements CanActivate, CanActivateChild, CanLoad
{
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Check the authenticated status
*
* @private
*/
private _check(): Observable<boolean>
{
// Check the authentication status
return this._authService.check()
.pipe(
switchMap((authenticated) => {
// If the user is authenticated...
if ( authenticated )
{
// Redirect to the root
this._router.navigate(['']);
// Prevent the access
return of(false);
}
// Allow the access
return of(true);
})
);
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
@@ -87,4 +55,36 @@ export class NoAuthGuard implements CanActivate, CanActivateChild, CanLoad
{
return this._check();
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Check the authenticated status
*
* @private
*/
private _check(): Observable<boolean>
{
// Check the authentication status
return this._authService.check()
.pipe(
switchMap((authenticated) => {
// If the user is authenticated...
if ( authenticated )
{
// Redirect to the root
this._router.navigate(['']);
// Prevent the access
return of(false);
}
// Allow the access
return of(true);
})
);
}
}

View File

@@ -38,6 +38,7 @@ export class CoreModule
// Register icon sets
this._matIconRegistry.addSvgIconSet(this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/material-twotone.svg'));
this._matIconRegistry.addSvgIconSetInNamespace('mat_outline', this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/material-outline.svg'));
this._matIconRegistry.addSvgIconSetInNamespace('mat_solid', this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/material-solid.svg'));
this._matIconRegistry.addSvgIconSetInNamespace('iconsmind', this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/iconsmind.svg'));
this._matIconRegistry.addSvgIconSetInNamespace('feather', this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/feather.svg'));
this._matIconRegistry.addSvgIconSetInNamespace('heroicons_outline', this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/heroicons-outline.svg'));

View File

@@ -31,14 +31,15 @@
*ngIf="results && !results.length">
No results found!
</mat-option>
<mat-option
class="group relative h-14 px-6 py-0 sm:px-8 text-md"
*ngFor="let result of results"
[routerLink]="result.link">
<ng-container
[ngTemplateOutlet]="searchResult"
[ngTemplateOutletContext]="{$implicit: result}"></ng-container>
</mat-option>
<ng-container *ngFor="let result of results">
<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>
</mat-autocomplete>
<button
class="absolute top-1/2 right-5 sm:right-7 flex-shrink-0 w-10 h-10 -mt-5"
@@ -72,14 +73,15 @@
*ngIf="results && !results.length">
No results found!
</mat-option>
<mat-option
class="group relative h-14 px-5 py-0 text-md"
*ngFor="let result of results"
[routerLink]="result.link">
<ng-container
[ngTemplateOutlet]="searchResult"
[ngTemplateOutletContext]="{$implicit: result}"></ng-container>
</mat-option>
<ng-container *ngFor="let result of results">
<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>
</mat-autocomplete>
</div>
</ng-container>

View File

@@ -3,14 +3,14 @@ import { FormControl } from '@angular/forms';
import { HttpClient } from '@angular/common/http';
import { Subject } from 'rxjs';
import { debounceTime, filter, map, takeUntil } from 'rxjs/operators';
import { FuseAnimations } from '@fuse/animations/public-api';
import { fuseAnimations } from '@fuse/animations/public-api';
@Component({
selector : 'search',
templateUrl : './search.component.html',
encapsulation: ViewEncapsulation.None,
exportAs : 'fuseSearch',
animations : FuseAnimations
animations : fuseAnimations
})
export class SearchComponent implements OnChanges, OnInit, OnDestroy
{
@@ -115,12 +115,9 @@ export class SearchComponent implements OnChanges, OnInit, OnDestroy
// Continue
return value;
}),
filter((value) => {
// Filter out undefined/null/false statements and also
// filter out the values that are smaller than minLength
return value && value.length >= this.minLength;
})
// Filter out undefined/null/false statements and also
// filter out the values that are smaller than minLength
filter(value => value && value.length >= this.minLength)
)
.subscribe((value) => {
this._httpClient.post('api/common/search', {query: value})

View File

@@ -28,9 +28,7 @@ import { SearchComponent } from 'app/layout/common/search/search.component';
providers : [
{
provide : MAT_AUTOCOMPLETE_SCROLL_STRATEGY,
useFactory: (overlay: Overlay) => {
return () => overlay.scrollStrategies.block();
},
useFactory: (overlay: Overlay) => () => overlay.scrollStrategies.block(),
deps : [Overlay]
}
]

View File

@@ -15,7 +15,9 @@ import { UserService } from 'app/core/user/user.service';
})
export class UserMenuComponent implements OnInit, OnDestroy
{
/* eslint-disable @typescript-eslint/naming-convention */
static ngAcceptInputType_showAvatar: BooleanInput;
/* eslint-enable @typescript-eslint/naming-convention */
@Input() showAvatar: boolean = true;
user: User;

View File

@@ -63,6 +63,7 @@
</ng-container>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<fuse-fullscreen></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts [shortcuts]="data.shortcuts"></shortcuts>
<messages [messages]="data.messages"></messages>

View File

@@ -5,6 +5,7 @@ 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 { FuseFullscreenModule } from '@fuse/components/fullscreen';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { MessagesModule } from 'app/layout/common/messages/messages.module';
import { NotificationsModule } from 'app/layout/common/notifications/notifications.module';
@@ -25,6 +26,7 @@ import { CenteredLayoutComponent } from 'app/layout/layouts/horizontal/centered/
MatDividerModule,
MatIconModule,
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
MessagesModule,
NotificationsModule,

View File

@@ -46,6 +46,7 @@
</ng-container>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<fuse-fullscreen></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts [shortcuts]="data.shortcuts"></shortcuts>
<messages [messages]="data.messages"></messages>

View File

@@ -5,6 +5,7 @@ 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 { FuseFullscreenModule } from '@fuse/components/fullscreen';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { MessagesModule } from 'app/layout/common/messages/messages.module';
import { NotificationsModule } from 'app/layout/common/notifications/notifications.module';
@@ -25,6 +26,7 @@ import { EnterpriseLayoutComponent } from 'app/layout/layouts/horizontal/enterpr
MatDividerModule,
MatIconModule,
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
MessagesModule,
NotificationsModule,

View File

@@ -52,6 +52,7 @@
</ng-container>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<fuse-fullscreen></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts [shortcuts]="data.shortcuts"></shortcuts>
<messages [messages]="data.messages"></messages>

View File

@@ -5,6 +5,7 @@ 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 { FuseFullscreenModule } from '@fuse/components/fullscreen';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { MessagesModule } from 'app/layout/common/messages/messages.module';
import { NotificationsModule } from 'app/layout/common/notifications/notifications.module';
@@ -25,6 +26,7 @@ import { MaterialLayoutComponent } from 'app/layout/layouts/horizontal/material/
MatDividerModule,
MatIconModule,
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
MessagesModule,
NotificationsModule,

View File

@@ -55,6 +55,7 @@
</ng-container>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<fuse-fullscreen></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts [shortcuts]="data.shortcuts"></shortcuts>
<messages [messages]="data.messages"></messages>

View File

@@ -5,6 +5,7 @@ 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 { FuseFullscreenModule } from '@fuse/components/fullscreen';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { MessagesModule } from 'app/layout/common/messages/messages.module';
import { NotificationsModule } from 'app/layout/common/notifications/notifications.module';
@@ -25,6 +26,7 @@ import { ModernLayoutComponent } from 'app/layout/layouts/horizontal/modern/mode
MatDividerModule,
MatIconModule,
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
MessagesModule,
NotificationsModule,

View File

@@ -36,6 +36,7 @@
</button>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<fuse-fullscreen></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts [shortcuts]="data.shortcuts"></shortcuts>
<messages [messages]="data.messages"></messages>

View File

@@ -5,6 +5,7 @@ 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 { FuseFullscreenModule } from '@fuse/components/fullscreen';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { MessagesModule } from 'app/layout/common/messages/messages.module';
import { NotificationsModule } from 'app/layout/common/notifications/notifications.module';
@@ -25,6 +26,7 @@ import { ClassicLayoutComponent } from 'app/layout/layouts/vertical/classic/clas
MatDividerModule,
MatIconModule,
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
MessagesModule,
NotificationsModule,

View File

@@ -66,6 +66,7 @@
</button>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<fuse-fullscreen></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts [shortcuts]="data.shortcuts"></shortcuts>
<messages [messages]="data.messages"></messages>
@@ -80,8 +81,8 @@
</div>
<!-- Footer -->
<div class="relative flex flex-0 items-center justify-start w-full h-14 px-4 md:px-6 z-49 border-t bg-card dark:bg-transparent print:hidden">
<!--<div class="relative flex flex-0 items-center justify-start w-full h-14 px-4 md:px-6 z-49 border-t bg-card dark:bg-transparent print:hidden">
<span class="font-medium text-secondary">Fuse &copy; {{currentYear}}</span>
</div>
</div>-->
</div>

View File

@@ -6,6 +6,7 @@ import { MatDividerModule } from '@angular/material/divider';
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 { 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';
@@ -25,6 +26,7 @@ import { ClassyLayoutComponent } from 'app/layout/layouts/vertical/classy/classy
MatDividerModule,
MatIconModule,
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
MessagesModule,
NotificationsModule,

View File

@@ -31,6 +31,7 @@
</button>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<fuse-fullscreen></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts [shortcuts]="data.shortcuts"></shortcuts>
<messages [messages]="data.messages"></messages>

View File

@@ -5,6 +5,7 @@ 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 { FuseFullscreenModule } from '@fuse/components/fullscreen';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { MessagesModule } from 'app/layout/common/messages/messages.module';
import { NotificationsModule } from 'app/layout/common/notifications/notifications.module';
@@ -25,6 +26,7 @@ import { CompactLayoutComponent } from 'app/layout/layouts/vertical/compact/comp
MatDividerModule,
MatIconModule,
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
MessagesModule,
NotificationsModule,

View File

@@ -40,6 +40,7 @@
</div>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<fuse-fullscreen></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts [shortcuts]="data.shortcuts"></shortcuts>
<messages [messages]="data.messages"></messages>

View File

@@ -5,6 +5,7 @@ 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 { FuseFullscreenModule } from '@fuse/components/fullscreen';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { MessagesModule } from 'app/layout/common/messages/messages.module';
import { NotificationsModule } from 'app/layout/common/notifications/notifications.module';
@@ -25,6 +26,7 @@ import { DenseLayoutComponent } from 'app/layout/layouts/vertical/dense/dense.co
MatDividerModule,
MatIconModule,
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
MessagesModule,
NotificationsModule,

View File

@@ -45,6 +45,7 @@
</button>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<fuse-fullscreen></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts [shortcuts]="data.shortcuts"></shortcuts>
<messages [messages]="data.messages"></messages>

View File

@@ -5,6 +5,7 @@ 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 { FuseFullscreenModule } from '@fuse/components/fullscreen';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { MessagesModule } from 'app/layout/common/messages/messages.module';
import { NotificationsModule } from 'app/layout/common/notifications/notifications.module';
@@ -25,6 +26,7 @@ import { FuturisticLayoutComponent } from 'app/layout/layouts/vertical/futuristi
MatDividerModule,
MatIconModule,
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
MessagesModule,
NotificationsModule,

View File

@@ -32,6 +32,7 @@
</button>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<fuse-fullscreen></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts [shortcuts]="data.shortcuts"></shortcuts>
<messages [messages]="data.messages"></messages>

View File

@@ -5,6 +5,7 @@ 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 { FuseFullscreenModule } from '@fuse/components/fullscreen';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { MessagesModule } from 'app/layout/common/messages/messages.module';
import { NotificationsModule } from 'app/layout/common/notifications/notifications.module';
@@ -25,6 +26,7 @@ import { ThinLayoutComponent } from 'app/layout/layouts/vertical/thin/thin.compo
MatDividerModule,
MatIconModule,
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
MessagesModule,
NotificationsModule,

View File

@@ -0,0 +1,89 @@
import { Injectable } from '@angular/core';
import { cloneDeep } from 'lodash-es';
import { FuseMockApiService } from '@fuse/lib/mock-api/mock-api.service';
import { categories as categoriesData, courses as coursesData, demoCourseSteps as demoCourseStepsData } from 'app/mock-api/apps/academy/data';
@Injectable({
providedIn: 'root'
})
export class AcademyMockApi
{
private _categories: any[] = categoriesData;
private _courses: any[] = coursesData;
private _demoCourseSteps: any[] = demoCourseStepsData;
/**
* Constructor
*/
constructor(private _fuseMockApiService: FuseMockApiService)
{
// Register Mock API handlers
this.registerHandlers();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Register Mock API handlers
*/
registerHandlers(): void
{
// -----------------------------------------------------------------------------------------------------
// @ Categories - GET
// -----------------------------------------------------------------------------------------------------
this._fuseMockApiService
.onGet('api/apps/academy/categories')
.reply(() => {
// Clone the categories
const categories = cloneDeep(this._categories);
// Sort the categories alphabetically by title
categories.sort((a, b) => a.title.localeCompare(b.title));
return [200, categories];
});
// -----------------------------------------------------------------------------------------------------
// @ Courses - GET
// -----------------------------------------------------------------------------------------------------
this._fuseMockApiService
.onGet('api/apps/academy/courses')
.reply(() => {
// Clone the courses
const courses = cloneDeep(this._courses);
return [200, courses];
});
// -----------------------------------------------------------------------------------------------------
// @ Course - GET
// -----------------------------------------------------------------------------------------------------
this._fuseMockApiService
.onGet('api/apps/academy/courses/course')
.reply(({request}) => {
// Get the id from the params
const id = request.params.get('id');
// Clone the courses and steps
const courses = cloneDeep(this._courses);
const steps = cloneDeep(this._demoCourseSteps);
// Find the course and attach steps to it
const course = courses.find(item => item.id === id);
if ( course )
{
course.steps = steps;
}
return [
200,
course
];
});
}
}

View File

@@ -0,0 +1,719 @@
/* eslint-disable */
export const categories = [
{
id : '9a67dff7-3c38-4052-a335-0cef93438ff6',
title: 'Web',
slug : 'web'
},
{
id : 'a89672f5-e00d-4be4-9194-cb9d29f82165',
title: 'Firebase',
slug : 'firebase'
},
{
id : '02f42092-bb23-4552-9ddb-cfdcc235d48f',
title: 'Cloud',
slug : 'cloud'
},
{
id : '5648a630-979f-4403-8c41-fc9790dea8cd',
title: 'Android',
slug : 'android'
}
];
export const courses = [
{
id : '694e4e5f-f25f-470b-bd0e-26b1d4f64028',
title : 'Basics of Angular',
slug : 'basics-of-angular',
description: 'Introductory course for Angular and framework basics',
category : 'web',
duration : 30,
totalSteps : 11,
updatedAt : 'Jun 28, 2021',
featured : true,
progress : {
currentStep: 3,
completed : 2
}
},
{
id : 'f924007a-2ee9-470b-a316-8d21ed78277f',
title : 'Basics of TypeScript',
slug : 'basics-of-typeScript',
description: 'Beginner course for Typescript and its basics',
category : 'web',
duration : 60,
totalSteps : 11,
updatedAt : 'Nov 01, 2021',
featured : true,
progress : {
currentStep: 5,
completed : 3
}
},
{
id : '0c06e980-abb5-4ba7-ab65-99a228cab36b',
title : 'Android N: Quick Settings',
slug : 'android-n-quick-settings',
description: 'Step by step guide for Android N: Quick Settings',
category : 'android',
duration : 120,
totalSteps : 11,
updatedAt : 'May 08, 2021',
featured : false,
progress : {
currentStep: 10,
completed : 1
}
},
{
id : '1b9a9acc-9a36-403e-a1e7-b11780179e38',
title : 'Build an App for the Google Assistant with Firebase',
slug : 'build-an-app-for-the-google-assistant-with-firebase',
description: 'Dive deep into Google Assistant apps using Firebase',
category : 'firebase',
duration : 30,
totalSteps : 11,
updatedAt : 'Jan 09, 2021',
featured : false,
progress : {
currentStep: 4,
completed : 3
}
},
{
id : '55eb415f-3f4e-4853-a22b-f0ae91331169',
title : 'Keep Sensitive Data Safe and Private',
slug : 'keep-sensitive-data-safe-and-private',
description: 'Learn how to keep your important data safe and private',
category : 'android',
duration : 45,
totalSteps : 11,
updatedAt : 'Jan 14, 2021',
featured : false,
progress : {
currentStep: 6,
completed : 0
}
},
{
id : 'fad2ab23-1011-4028-9a54-e52179ac4a50',
title : 'Manage Your Pivotal Cloud Foundry App\'s Using Apigee Edge',
slug : 'manage-your-pivotal-cloud-foundry-apps-using-apigee-Edge',
description: 'Introductory course for Pivotal Cloud Foundry App',
category : 'cloud',
duration : 90,
totalSteps : 11,
updatedAt : 'Jun 24, 2021',
featured : false,
progress : {
currentStep: 6,
completed : 0
}
},
{
id : 'c4bc107b-edc4-47a7-a7a8-4fb09732e794',
title : 'Build a PWA Using Workbox',
slug : 'build-a-pwa-using-workbox',
description: 'Step by step guide for building a PWA using Workbox',
category : 'web',
duration : 120,
totalSteps : 11,
updatedAt : 'Nov 19, 2021',
featured : false,
progress : {
currentStep: 0,
completed : 0
}
},
{
id : '1449f945-d032-460d-98e3-406565a22293',
title : 'Cloud Functions for Firebase',
slug : 'cloud-functions-for-firebase',
description: 'Beginners guide of Firebase Cloud Functions',
category : 'firebase',
duration : 45,
totalSteps : 11,
updatedAt : 'Jul 11, 2021',
featured : false,
progress : {
currentStep: 3,
completed : 1
}
},
{
id : 'f05e08ab-f3e3-4597-a032-6a4b69816f24',
title : 'Building a gRPC Service with Java',
slug : 'building-a-grpc-service-with-java',
description: 'Learn more about building a gRPC Service with Java',
category : 'cloud',
duration : 30,
totalSteps : 11,
updatedAt : 'Mar 13, 2021',
featured : false,
progress : {
currentStep: 0,
completed : 1
}
},
{
id : '181728f4-87c8-45c5-b9cc-92265bcd2f4d',
title : 'Looking at Campaign Finance with BigQuery',
slug : 'looking-at-campaign-finance-with-bigquery',
description: 'Dive deep into BigQuery: Campaign Finance',
category : 'cloud',
duration : 60,
totalSteps : 11,
updatedAt : 'Nov 01, 2021',
featured : false,
progress : {
currentStep: 0,
completed : 0
}
},
{
id : 'fcbfedbf-6187-4b3b-89d3-1a7cb4e11616',
title : 'Personalize Your iOS App with Firebase User Management',
slug : 'personalize-your-ios-app-with-firebase-user-management',
description: 'Dive deep into User Management on iOS apps using Firebase',
category : 'firebase',
duration : 90,
totalSteps : 11,
updatedAt : 'Aug 08, 2021',
featured : false,
progress : {
currentStep: 0,
completed : 0
}
},
{
id : '5213f6a1-1dd7-4b1d-b6e9-ffb7af534f28',
title : 'Customize Network Topology with Subnetworks',
slug : 'customize-network-topology-with-subnetworks',
description: 'Dive deep into Network Topology with Subnetworks',
category : 'web',
duration : 45,
totalSteps : 11,
updatedAt : 'May 12, 2021',
featured : false,
progress : {
currentStep: 0,
completed : 0
}
},
{
id : '02992ac9-d1a3-4167-b70e-8a1d5b5ba253',
title : 'Building Beautiful UIs with Flutter',
slug : 'building-beautiful-uis-with-flutter',
description: 'Dive deep into Flutter\'s hidden secrets for creating beautiful UIs',
category : 'web',
duration : 90,
totalSteps : 11,
updatedAt : 'Sep 18, 2021',
featured : false,
progress : {
currentStep: 8,
completed : 2
}
},
{
id : '2139512f-41fb-4a4a-841a-0b4ac034f9b4',
title : 'Firebase Android',
slug : 'firebase-android',
description: 'Beginners guide of Firebase for Android',
category : 'android',
duration : 45,
totalSteps : 11,
updatedAt : 'Apr 24, 2021',
featured : false,
progress : {
currentStep: 0,
completed : 0
}
},
{
id : '65e0a0e0-d8c0-4117-a3cb-eb74f8e28809',
title : 'Simulating a Thread Network Using OpenThread',
slug : 'simulating-a-thread-network-using-openthread',
description: 'Introductory course for OpenThread and Simulating a Thread Network',
category : 'web',
duration : 45,
totalSteps : 11,
updatedAt : 'Jun 05, 2021',
featured : false,
progress : {
currentStep: 0,
completed : 0
}
},
{
id : 'c202ebc9-9be3-433a-9d38-7003b3ed7b7a',
title : 'Your First Progressive Web App',
slug : 'your-first-progressive-web-app',
description: 'Step by step guide for creating a PWA from scratch',
category : 'web',
duration : 30,
totalSteps : 11,
updatedAt : 'Oct 14, 2021',
featured : false,
progress : {
currentStep: 0,
completed : 0
}
},
{
id : '980ae7da-9f77-4e30-aa98-1b1ea594e775',
title : 'Launch Cloud Datalab',
slug : 'launch-cloud-datalab',
description: 'From start to finish: Launch Cloud Datalab',
category : 'cloud',
duration : 60,
totalSteps : 11,
updatedAt : 'Dec 16, 2021',
featured : false,
progress : {
currentStep: 0,
completed : 0
}
},
{
id : 'c9748ea9-4117-492c-bdb2-55085b515978',
title : 'Cloud Firestore',
slug : 'cloud-firestore',
description: 'Step by step guide for setting up Cloud Firestore',
category : 'firebase',
duration : 90,
totalSteps : 11,
updatedAt : 'Apr 04, 2021',
featured : false,
progress : {
currentStep: 2,
completed : 0
}
}
];
export const demoCourseContent = `
<p class="lead">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus aperiam lab et fugiat id magnam minus nemo quam
voluptatem. Culpa deleniti explica nisi quod soluta.
</p>
<p>
Alias animi labque, deserunt distinctio eum excepturi fuga iure labore magni molestias mollitia natus, officia pofro
quis sunt temporibus veritatis voluptatem, voluptatum. Aut blanditiis esse et illum maxim, obcaecati possimus
voluptate! Accusamus <em>adipisci</em> amet aperiam, assumenda consequuntur fugiat inventore iusto magnam molestias
natus necessitatibus, nulla pariatur.
</p>
<p>
Amet distinctio enim itaque minima minus nesciunt recusandae soluta voluptatibus:
</p>
<blockquote>
<p>
Ad aliquid amet asperiores lab distinctio doloremque <code>eaque</code>, exercitationem explicabo, minus mollitia
natus necessitatibus odio omnis pofro rem.
</p>
</blockquote>
<p>
Alias architecto asperiores, dignissimos illum ipsam ipsum itaque, natus necessitatibus officiis, perferendis quae
sed ullam veniam vitae voluptas! Magni, nisi, quis! A <code>accusamus</code> animi commodi, consectetur distinctio
eaque, eos excepturi illum laboriosam maiores nam natus nulla officiis perspiciatis rem <em>reprehenderit</em> sed
tenetur veritatis.
</p>
<p>
Consectetur <code>dicta enim</code> error eveniet expedita, facere in itaque labore <em>natus</em> quasi? Ad consectetur
eligendi facilis magni quae quis, quo temporibus voluptas voluptate voluptatem!
</p>
<p>
Adipisci alias animi <code>debitis</code> eos et impedit maiores, modi nam nobis officia optio perspiciatis, rerum.
Accusantium esse nostrum odit quis quo:
</p>
<pre><code>h1 a {{'{'}}
display: block;
width: 300px;
height: 80px;
{{'}'}}</code></pre>
<p>
Accusantium aut autem, lab deleniti eaque fugiat fugit id ipsa iste molestiae,
<a>necessitatibus nemo quasi</a>
.
</p>
<hr>
<h2>
Accusantium aspernatur autem enim
</h2>
<p>
Blanditiis, fugit voluptate! Assumenda blanditiis consectetur, labque cupiditate ducimus eaque earum, fugiat illum
ipsa, necessitatibus omnis quaerat reiciendis totam. Architecto, <strong>facere</strong> illum molestiae nihil nulla
quibusdam quidem vel! Atque <em>blanditiis deserunt</em>.
</p>
<p>
Debitis deserunt doloremque labore laboriosam magni minus odit:
</p>
<ol>
<li>Asperiores dicta esse maiores nobis officiis.</li>
<li>Accusamus aliquid debitis dolore illo ipsam molettiae possimus.</li>
<li>Magnam mollitia pariatur perspiciatis quasi quidem tenetur voluptatem! Adipisci aspernatur assumenda dicta.</li>
</ol>
<p>
Animi fugit incidunt iure magni maiores molestias.
</p>
<h3>
Consequatur iusto soluta
</h3>
<p>
Aliquid asperiores corporis — deserunt dolorum ducimus eius eligendi explicabo quaerat suscipit voluptas.
</p>
<p>
Deserunt dolor eos et illum laborum magni molestiae mollitia:
</p>
<blockquote>
<p>Autem beatae consectetur consequatur, facere, facilis fugiat id illo, impedit numquam optio quis sunt ducimus illo.</p>
</blockquote>
<p>
Adipisci consequuntur doloribus facere in ipsam maxime molestias pofro quam:
</p>
<figure>
<img
src="assets/images/pages/help-center/image-1.jpg"
alt="">
<figcaption>
Accusamus blanditiis labque delectus esse et eum excepturi, impedit ipsam iste maiores minima mollitia, nihil obcaecati
placeat quaerat qui quidem sint unde!
</figcaption>
</figure>
<p>
A beatae lab deleniti explicabo id inventore magni nisi omnis placeat praesentium quibusdam:
</p>
<ul>
<li>Dolorem eaque laboriosam omnis praesentium.</li>
<li>Atque debitis delectus distinctio doloremque.</li>
<li>Fuga illo impedit minima mollitia neque obcaecati.</li>
</ul>
<p>
Consequ eius eum excepturi explicabo.
</p>
<h2>
Adipisicing elit atque impedit?
</h2>
<h3>
Atque distinctio doloremque ea qui quo, repellendus.
</h3>
<p>
Delectus deserunt explicabo facilis numquam quasi! Laboriosam, magni, quisquam. Aut, blanditiis commodi distinctio, facere fuga
hic itaque iure labore laborum maxime nemo neque provident quos recusandae sequi veritatis illum inventore iure qui rerum sapiente.
</p>
<h3>
Accusamus iusto sint aperiam consectetur …
</h3>
<p>
Aliquid assumenda ipsa nam odit pofro quaerat, quasi recusandae sint! Aut, esse explicabo facilis fugit illum iure magni
necessitatibus odio quas.
</p>
<ul>
<li>
<p><strong>Dolore natus placeat rem atque dignissimos laboriosam.</strong></p>
<p>
Amet repudiandae voluptates architecto dignissimos repellendus voluptas dignissimos eveniet itaque maiores natus.
</p>
<p>
Accusamus aliquam debitis delectus dolorem ducimus enim eos, exercitationem fugiat id iusto nostrum quae quos
recusandae reiciendis rerum sequi temporibus veniam vero? Accusantium culpa, cupiditate ducimus eveniet id maiores modi
mollitia nisi aliquid dolorum ducimus et illo in.
</p>
</li>
<li>
<p><strong>Ab amet deleniti dolor, et hic optio placeat.</strong></p>
<p>
Accusantium ad alias beatae, consequatur consequuntur eos error eveniet expedita fuga laborum libero maxime nulla pofro
praesentium rem rerum saepe soluta ullam vero, voluptas? Architecto at debitis, doloribus harum iure libero natus odio
optio soluta veritatis voluptate.
</p>
</li>
<li>
<p><strong>At aut consectetur nam necessitatibus neque nesciunt.</strong></p>
<p>
Aut dignissimos labore nobis nostrum optio! Dolor id minima velit voluptatibus. Aut consequuntur eum exercitationem
fuga, harum id impedit molestiae natus neque numquam perspiciatis quam rem voluptatum.
</p>
</li>
</ul>
<p>
Animi aperiam autem labque dolore enim ex expedita harum hic id impedit ipsa laborum modi mollitia non perspiciatis quae ratione.
</p>
<h2>
Alias eos excepturi facilis fugit.
</h2>
<p>
Alias asperiores, aspernatur corporis
<a>delectus</a>
est
<a>facilis</a>
inventore dolore
ipsa nobis nostrum officia quia, veritatis vero! At dolore est nesciunt numquam quam. Ab animi <em>architecto</em> aut, dignissimos
eos est eum explicabo.
</p>
<p>
Adipisci autem consequuntur <code>labque cupiditate</code> dolor ducimus fuga neque nesciunt:
</p>
<pre><code>module.exports = {{'{'}}
purge: [],
theme: {{'{'}}
extend: {{'{}'}},
},
variants: {{'{}'}},
plugins: [],
{{'}'}}</code></pre>
<p>
Aliquid aspernatur eius fugit hic iusto.
</p>
<h3>
Dolorum ducimus expedita?
</h3>
<p>
Culpa debitis explicabo maxime minus quaerat reprehenderit temporibus! Asperiores, cupiditate ducimus esse est expedita fuga hic
ipsam necessitatibus placeat possimus? Amet animi aut consequuntur earum eveniet.
</p>
<ol>
<li>
<strong>Aspernatur at beatae corporis debitis.</strong>
<ul>
<li>
Aperiam assumenda commodi lab dicta eius, “fugit ipsam“ itaque iure molestiae nihil numquam, officia omnis quia
repellendus sapiente sed.
</li>
<li>
Nulla odio quod saepe accusantium, adipisci autem blanditiis lab doloribus.
</li>
<li>
Explicabo facilis iusto molestiae nisi nostrum obcaecati officia.
</li>
</ul>
</li>
<li>
<strong>Nobis odio officiis optio quae quis quisquam quos rem.</strong>
<ul>
<li>Modi pariatur quod totam. Deserunt doloribus eveniet, expedita.</li>
<li>Ad beatae dicta et fugit libero optio quaerat rem repellendus./</li>
<li>Architecto atque consequuntur corporis id iste magni.</li>
</ul>
</li>
<li>
<strong>Deserunt non placeat unde veniam veritatis? Odio quod.</strong>
<ul>
<li>Inventore iure magni quod repellendus tempora. Magnam neque, quia. Adipisci amet.</li>
<li>Consectetur adipisicing elit.</li>
<li>labque eum expedita illo inventore iusto laboriosam nesciunt non, odio provident.</li>
</ul>
</li>
</ol>
<p>
A aliquam architecto consequatur labque dicta doloremque <code>&lt;li&gt;</code> doloribus, ducimus earum, est <code>&lt;p&gt;</code>
eveniet explicabo fuga fugit ipsum minima minus molestias nihil nisi non qui sunt vel voluptatibus? A dolorum illum nihil quidem.
</p>
<ul>
<li>
<p>
<strong>Laboriosam nesciunt obcaecati optio qui.</strong>
</p>
<p>
Doloremque magni molestias reprehenderit.
</p>
<ul>
<li>Accusamus aperiam blanditiis <code>&lt;p&gt;</code> commodi</li>
<li>Dolorum ea explicabo fugiat in ipsum</li>
</ul>
</li>
<li>
<p>
<strong>Commodi dolor dolorem dolores eum expedita libero.</strong>
</p>
<p>
Accusamus alias consectetur dolores et, excepturi fuga iusto possimus.
</p>
<ul>
<li>
<p>
Accusantium ad alias atque aut autem consequuntur deserunt dignissimos eaque iure <code>&lt;p&gt;</code> maxime.
</p>
<p>
Dolorum in nisi numquam omnis quam sapiente sit vero.
</p>
</li>
<li>
<p>
Adipisci lab in nisi odit soluta sunt vitae commodi excepturi.
</p>
</li>
</ul>
</li>
<li>
<p>
Assumenda deserunt distinctio dolor iste mollitia nihil non?
</p>
</li>
</ul>
<p>
Consectetur adipisicing elit dicta dolor iste.
</p>
<h2>
Consectetur ea natus officia omnis reprehenderit.
</h2>
<p>
Distinctio impedit quaerat sed! Accusamus
<a>aliquam aspernatur enim expedita explicabo</a>
. Libero molestiae
odio quasi unde ut? Ab exercitationem id numquam odio quisquam!
</p>
<p>
Explicabo facilis nemo quidem natus tempore:
</p>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Wrestler</th>
<th>Origin</th>
<th>Finisher</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bret “The Hitman” Hart</td>
<td>Calgary, AB</td>
<td>Sharpshooter</td>
</tr>
<tr>
<td>Stone Cold Steve Austin</td>
<td>Austin, TX</td>
<td>Stone Cold Stunner</td>
</tr>
<tr>
<td>Randy Savage</td>
<td>Sarasota, FL</td>
<td>Elbow Drop</td>
</tr>
<tr>
<td>Vader</td>
<td>Boulder, CO</td>
<td>Vader Bomb</td>
</tr>
<tr>
<td>Razor Ramon</td>
<td>Chuluota, FL</td>
<td>Razors Edge</td>
</tr>
</tbody>
</table>
<p>
A aliquid autem lab doloremque, ea earum eum fuga fugit illo ipsa minus natus nisi <code>&lt;span&gt;</code> obcaecati pariatur
perferendis pofro <code>suscipit tempore</code>.
</p>
<h3>
Ad alias atque culpa <code>illum</code> earum optio
</h3>
<p>
Architecto consequatur eveniet illo in iure laborum minus omnis quibusdam sequi temporibus? Ab aliquid <em>“atque dolores molestiae
nemo perferendis”</em> reprehenderit saepe.
</p>
<p>
Accusantium aliquid eligendi est fuga natus, <code>quos</code> vel? Adipisci aperiam asperiores aspernatur consectetur cupiditate
<a><code>@distinctio/doloribus</code></a>
et exercitationem expedita, facere facilis illum, impedit inventore
ipsa iure iusto magnam, magni minus nesciunt non officia possimus quod reiciendis.
</p>
<h4>
Cupiditate explicabo <code>hic</code> maiores
</h4>
<p>
Aliquam amet consequuntur distinctio <code>ea</code> est <code>excepturi</code> facere illum maiores nisi nobis non odit officiis
quisquam, similique tempora temporibus, tenetur ullam <code>voluptates</code> adipisci aperiam deleniti <code>doloremque</code>
ducimus <code>eos</code>.
</p>
<p>
Ducimus qui quo tempora. lab enim explicabo <code>hic</code> inventore qui soluta voluptates voluptatum? Asperiores consectetur
delectus dolorem fugiat ipsa pariatur, quas <code>quos</code> repellendus <em>repudiandae</em> sunt aut blanditiis.
</p>
<h3>
Asperiores aspernatur autem error praesentium quidem.
</h3>
<h4>
Ad blanditiis commodi, doloribus id iste <code>repudiandae</code> vero vitae.
</h4>
<p>
Atque consectetur lab debitis enim est et, facere fugit impedit, possimus quaerat quibusdam.
</p>
<p>
Dolorem nihil placeat quibusdam veniam? Amet architecto at consequatur eligendi eveniet excepturi hic illo impedit in iste magni maxime
modi nisi nulla odio placeat quidem, quos rem repellat similique suscipit voluptate voluptates nobis omnis quo repellendus.
</p>
<p>
Assumenda, eum, minima! Autem consectetur fugiat iste sit! Nobis omnis quo repellendus.
</p>
`;
export const demoCourseSteps = [
{
order : 0,
title : 'Introduction',
subtitle: 'Introducing the library and how it works',
content : `<h2 class="text-2xl sm:text-3xl">Introduction</h1> ${demoCourseContent}`
},
{
order : 1,
title : 'Get the sample code',
subtitle: 'Where to find the sample code and how to access it',
content : `<h2 class="text-2xl sm:text-3xl">Get the sample code</h1> ${demoCourseContent}`
},
{
order : 2,
title : 'Create a Firebase project and Set up your app',
subtitle: 'How to create a basic Firebase project and how to run it locally',
content : `<h2 class="text-2xl sm:text-3xl">Create a Firebase project and Set up your app</h1> ${demoCourseContent}`
},
{
order : 3,
title : 'Install the Firebase Command Line Interface',
subtitle: 'Setting up the Firebase CLI to access command line tools',
content : `<h2 class="text-2xl sm:text-3xl">Install the Firebase Command Line Interface</h1> ${demoCourseContent}`
},
{
order : 4,
title : 'Deploy and run the web app',
subtitle: 'How to build, push and run the project remotely',
content : `<h2 class="text-2xl sm:text-3xl">Deploy and run the web app</h1> ${demoCourseContent}`
},
{
order : 5,
title : 'The Functions Directory',
subtitle: 'Introducing the Functions and Functions Directory',
content : `<h2 class="text-2xl sm:text-3xl">The Functions Directory</h1> ${demoCourseContent}`
},
{
order : 6,
title : 'Import the Cloud Functions and Firebase Admin modules',
subtitle: 'Create your first Function and run it to administer your app',
content : `<h2 class="text-2xl sm:text-3xl">Import the Cloud Functions and Firebase Admin modules</h1> ${demoCourseContent}`
},
{
order : 7,
title : 'Welcome New Users',
subtitle: 'How to create a welcome message for the new users',
content : `<h2 class="text-2xl sm:text-3xl">Welcome New Users</h1> ${demoCourseContent}`
},
{
order : 8,
title : 'Images moderation',
subtitle: 'How to moderate images; crop, resize, optimize',
content : `<h2 class="text-2xl sm:text-3xl">Images moderation</h1> ${demoCourseContent}`
},
{
order : 9,
title : 'New Message Notifications',
subtitle: 'How to create and push a notification to a user',
content : `<h2 class="text-2xl sm:text-3xl">New Message Notifications</h1> ${demoCourseContent}`
},
{
order : 10,
title : 'Congratulations!',
subtitle: 'Nice work, you have created your first application',
content : `<h2 class="text-2xl sm:text-3xl">Congratulations!</h1> ${demoCourseContent}`
}
];

View File

@@ -110,11 +110,11 @@ export class CalendarMockApi
const id = request.params.get('id');
// Find the calendar and delete it
const index = this._calendars.findIndex((calendar) => calendar.id === id);
const index = this._calendars.findIndex(calendar => calendar.id === id);
this._calendars.splice(index, 1);
// Find the events that belong to the calendar and remove them as well
this._events = this._events.filter((event) => event.calendarId !== id);
this._events = this._events.filter(event => event.calendarId !== id);
// Return the response
return [200, true];
@@ -287,7 +287,7 @@ export class CalendarMockApi
const id = request.params.get('id');
// Find the event and delete it
const index = this._events.findIndex((item) => item.id === id);
const index = this._events.findIndex(item => item.id === id);
this._events.splice(index, 1);
// Return the response
@@ -307,7 +307,7 @@ export class CalendarMockApi
const mode = request.body.mode;
// Find the original recurring event from db
const recurringEvent = this._events.find((item) => item.id === event.recurringEventId);
const recurringEvent = this._events.find(item => item.id === event.recurringEventId);
// Single
if ( mode === 'single' )
@@ -406,7 +406,7 @@ export class CalendarMockApi
if ( mode === 'all' )
{
// Find the event index
const eventIndex = this._events.findIndex((item) => item.id === event.recurringEventId);
const eventIndex = this._events.findIndex(item => item.id === event.recurringEventId);
// Update the recurring event
this._events[eventIndex] = assign({}, this._events[eventIndex], omit(event, ['id', 'recurringEventId', 'range']));
@@ -428,7 +428,7 @@ export class CalendarMockApi
const mode = request.params.get('mode');
// Find the recurring event
const recurringEvent = this._events.find((item) => item.id === event.recurringEventId);
const recurringEvent = this._events.find(item => item.id === event.recurringEventId);
// Single
if ( mode === 'single' )
@@ -495,7 +495,7 @@ export class CalendarMockApi
if ( mode === 'all' )
{
// Find the event and delete it
const index = this._events.findIndex((item) => item.id === event.recurringEventId);
const index = this._events.findIndex(item => item.id === event.recurringEventId);
this._events.splice(index, 1);
}

View File

@@ -1,6 +1,6 @@
/* eslint-disable */
import * as moment from 'moment';
/* tslint:disable:max-line-length */
export const calendars = [
{
id : '1a470c8e-40ed-4c2d-b590-a4f1f6ead6cc',

View File

@@ -0,0 +1,167 @@
import { Injectable } from '@angular/core';
import { assign, cloneDeep, omit } from 'lodash-es';
import { FuseMockApiService } from '@fuse/lib/mock-api';
import { chats as chatsData, contacts as contactsData, messages as messagesData, profile as profileData } from 'app/mock-api/apps/chat/data';
@Injectable({
providedIn: 'root'
})
export class ChatMockApi
{
private _chats: any[] = chatsData;
private _contacts: any[] = contactsData;
private _messages: any[] = messagesData;
private _profile: any = profileData;
/**
* Constructor
*/
constructor(private _fuseMockApiService: FuseMockApiService)
{
// Register Mock API handlers
this.registerHandlers();
// Modify the chats array to attach certain data to it
this._chats = this._chats.map(chat => ({
...chat,
// Get the actual contact object from the id and attach it to the chat
contact: this._contacts.find(contact => contact.id === chat.contactId),
// Since we use same set of messages on all chats, we assign them here.
messages: this._messages.map(message => ({
...message,
chatId : chat.id,
contactId: message.contactId === 'me' ? this._profile.id : chat.contactId,
isMine : message.contactId === 'me'
}))
}));
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Register Mock API handlers
*/
registerHandlers(): void
{
// -----------------------------------------------------------------------------------------------------
// @ Chats - GET
// -----------------------------------------------------------------------------------------------------
this._fuseMockApiService
.onGet('api/apps/chat/chats')
.reply(() => {
// Clone the chats
const chats = cloneDeep(this._chats);
// Return the response
return [200, chats];
});
// -----------------------------------------------------------------------------------------------------
// @ Chat - GET
// -----------------------------------------------------------------------------------------------------
this._fuseMockApiService
.onGet('api/apps/chat/chat')
.reply(({request}) => {
// Get the chat id
const id = request.params.get('id');
// Clone the chats
const chats = cloneDeep(this._chats);
// Find the chat we need
const chat = chats.find(item => item.id === id);
// Return the response
return [200, chat];
});
// -----------------------------------------------------------------------------------------------------
// @ Chat - PATCH
// -----------------------------------------------------------------------------------------------------
this._fuseMockApiService
.onPatch('api/apps/chat/chat')
.reply(({request}) => {
// Get the id and chat
const id = request.body.id;
const chat = cloneDeep(request.body.chat);
// Prepare the updated chat
let updatedChat = null;
// Find the chat and update it
this._chats.forEach((item, index, chats) => {
if ( item.id === id )
{
// Update the chat
chats[index] = assign({}, chats[index], chat);
// Store the updated chat
updatedChat = chats[index];
}
});
// Return the response
return [200, updatedChat];
});
// -----------------------------------------------------------------------------------------------------
// @ Contacts - GET
// -----------------------------------------------------------------------------------------------------
this._fuseMockApiService
.onGet('api/apps/chat/contacts')
.reply(() => {
// Clone the contacts
let contacts = cloneDeep(this._contacts);
// Sort the contacts by the name field by default
contacts.sort((a, b) => a.name.localeCompare(b.name));
// Omit details and attachments from contacts
contacts = contacts.map(contact => omit(contact, ['details', 'attachments']));
// Return the response
return [200, contacts];
});
// -----------------------------------------------------------------------------------------------------
// @ Contact Details - GET
// -----------------------------------------------------------------------------------------------------
this._fuseMockApiService
.onGet('api/apps/chat/contact')
.reply(({request}) => {
// Get the contact id
const id = request.params.get('id');
// Clone the contacts
const contacts = cloneDeep(this._contacts);
// Find the contact
const contact = contacts.find(item => item.id === id);
// Return the response
return [200, contact];
});
// -----------------------------------------------------------------------------------------------------
// @ Profile - GET
// -----------------------------------------------------------------------------------------------------
this._fuseMockApiService
.onGet('api/apps/chat/profile')
.reply(() => {
// Clone the profile
const profile = cloneDeep(this._profile);
// Return the response
return [200, profile];
});
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -66,7 +66,7 @@ export class ContactsMockApi
if ( query )
{
// Filter the contacts
contacts = contacts.filter((contact) => contact.name && contact.name.toLowerCase().includes(query.toLowerCase()));
contacts = contacts.filter(contact => contact.name && contact.name.toLowerCase().includes(query.toLowerCase()));
}
// Sort the contacts by the name field by default
@@ -90,9 +90,7 @@ export class ContactsMockApi
const contacts = cloneDeep(this._contacts);
// Find the contact
const contact = contacts.find((item) => {
return item.id === id;
});
const contact = contacts.find(item => item.id === id);
// Return the response
return [200, contact];
@@ -288,10 +286,10 @@ export class ContactsMockApi
*
* @param file
*/
const readAsDataURL = (file: File): Promise<any> => {
const readAsDataURL = (file: File): Promise<any> =>
// Return a new promise
return new Promise((resolve, reject) => {
new Promise((resolve, reject) => {
// Create a new reader
const reader = new FileReader();
@@ -308,8 +306,8 @@ export class ContactsMockApi
// Read the file as the
reader.readAsDataURL(file);
});
};
})
;
this._fuseMockApiService
.onPost('api/apps/contacts/avatar')

File diff suppressed because it is too large Load Diff

View File

@@ -81,9 +81,7 @@ export class ECommerceInventoryMockApi
if ( search )
{
// Filter the products
products = products.filter((contact) => {
return contact.name && contact.name.toLowerCase().includes(search.toLowerCase());
});
products = products.filter(contact => contact.name && contact.name.toLowerCase().includes(search.toLowerCase()));
}
// Paginate - Start
@@ -148,9 +146,7 @@ export class ECommerceInventoryMockApi
const products = cloneDeep(this._products);
// Find the product
const product = products.find((item) => {
return item.id === id;
});
const product = products.find(item => item.id === id);
// Return the response
return [200, product];

View File

@@ -1,4 +1,4 @@
/* tslint:disable:max-line-length */
/* eslint-disable */
export const categories = [
{
id : 'b899ec30-b85a-40ab-bb1f-18a596d5c6de',

View File

@@ -39,8 +39,8 @@ export class FileManagerMockApi
const items = cloneDeep(this._items);
// Separate the items by folders and files
const folders = items.filter((item) => item.type === 'folder');
const files = items.filter((item) => item.type !== 'folder');
const folders = items.filter(item => item.type === 'folder');
const files = items.filter(item => item.type !== 'folder');
// Sort the folders and files alphabetically by filename
folders.sort((a, b) => a.name.localeCompare(b.name));

View File

@@ -1,4 +1,4 @@
/* tslint:disable:max-line-length */
/* eslint-disable */
export const items = [
{
id : 'cd6897cb-acfd-4016-8b53-3f66a5b5fc68',
@@ -175,5 +175,5 @@ export const items = [
type : 'XLS',
contents : null,
description: null
},
}
];

View File

@@ -1,4 +1,4 @@
/* tslint:disable:max-line-length */
/* eslint-disable */
export const faqCategories = [
{
id : '28924eab-97cc-465a-ba21-f232bb95843f',

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