Compare commits

..

12 Commits

Author SHA1 Message Date
sercan
d146a92c79 Merge remote-tracking branch 'origin/demo' into starter 2021-05-15 13:18:13 +03:00
sercan
27b6858b76 Merge remote-tracking branch 'origin/demo' into starter 2021-05-06 17:12:50 +03:00
sercan
fcfba4c9e4 Merge remote-tracking branch 'origin/demo' into starter 2021-04-30 19:40:30 +03:00
sercan
40894e0aa3 Merge remote-tracking branch 'origin/demo' into starter
# Conflicts:
#	src/app/app.routing.ts
#	src/app/mock-api/common/navigation/data.ts
#	src/app/modules/admin/apps/academy/academy.service.ts
#	src/app/modules/admin/apps/academy/details/details.component.html
#	src/app/modules/admin/apps/academy/list/list.component.html
#	src/app/modules/admin/apps/mailbox/list/list.component.ts
#	src/app/modules/admin/docs/changelog/changelog.ts
#	src/app/modules/admin/pages/pricing/modern/modern.component.html
#	src/app/modules/admin/pages/pricing/simple/simple.component.html
#	src/app/modules/admin/pages/pricing/single/single.component.html
#	src/app/modules/admin/pages/pricing/table/table.component.html
2021-04-30 19:40:07 +03:00
sercan
8dcf21cb1a Merge remote-tracking branch 'origin/demo' into starter 2021-04-26 10:23:15 +03:00
sercan
d917f03883 Merge remote-tracking branch 'origin/demo' into starter 2021-04-26 10:20:06 +03:00
sercan
0f2ddbda83 Merge remote-tracking branch 'origin/demo' into starter
# Conflicts:
#	src/app/mock-api/common/navigation/data.ts
#	src/app/modules/admin/docs/changelog/changelog.ts
2021-04-26 09:56:44 +03:00
sercan
fa0d74504b Merge remote-tracking branch 'origin/demo' into starter 2021-04-26 09:56:29 +03:00
sercan
ad2b19a07a Merge remote-tracking branch 'origin/demo' into starter
# Conflicts:
#	src/app/app.routing.ts
#	src/app/mock-api/common/navigation/data.ts
#	src/app/modules/admin/apps/contacts/details/details.component.html
#	src/app/modules/admin/apps/file-manager/list/list.component.html
#	src/app/modules/admin/apps/file-manager/list/list.component.ts
#	src/app/modules/landing/home/home.component.html
2021-04-26 09:31:42 +03:00
sercan
4bf11591a2 (Assets) Added avatar images back 2021-04-19 13:08:24 +03:00
sercan
f45a605b4e Preparing the starter 2021-04-15 17:43:28 +03:00
sercan
c150a8902c Starter 2021-04-15 17:23:49 +03:00
794 changed files with 351 additions and 69418 deletions

View File

@@ -16,7 +16,8 @@
],
"parserOptions": {
"project": [
"tsconfig.json"
"tsconfig.json",
"e2e/tsconfig.json"
],
"createDefaultProgram": true
},
@@ -43,7 +44,6 @@
}
],
"@typescript-eslint/dot-notation": "off",
"@typescript-eslint/explicit-function-return-type": "error",
"@typescript-eslint/explicit-member-accessibility": [
"off",
{

View File

@@ -2,9 +2,6 @@
// @ 3rd party credits
// -----------------------------------------------------------------------------------------------------
// Flags
https://github.com/Yummygum/flagpack-core
// Icons
Material - https://material.io/tools/icons
Feather - https://feathericons.com/

536
package-lock.json generated
View File

@@ -453,6 +453,7 @@
"version": "7.12.13",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz",
"integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==",
"dev": true,
"requires": {
"@babel/highlight": "^7.12.13"
}
@@ -747,7 +748,8 @@
"@babel/helper-validator-identifier": {
"version": "7.14.0",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz",
"integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A=="
"integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==",
"dev": true
},
"@babel/helper-validator-option": {
"version": "7.12.17",
@@ -782,6 +784,7 @@
"version": "7.14.0",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz",
"integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==",
"dev": true,
"requires": {
"@babel/helper-validator-identifier": "^7.14.0",
"chalk": "^2.0.0",
@@ -1612,9 +1615,9 @@
"dev": true
},
"@es-joy/jsdoccomment": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.6.0.tgz",
"integrity": "sha512-zT1EtysKMITJ7vE4RvOJqitxk/Str6It8hq+fykxkwLuTyzgak+TnVuVSIyovT/qrEz3i46ypCSXgNtIDYwNOg==",
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.4.4.tgz",
"integrity": "sha512-ua4qDt9dQb4qt5OI38eCZcQZYE5Bq3P0GzgvDARdT8Lt0mAUpxKTPy8JGGqEvF77tG1irKDZ3WreeezEa3P43w==",
"dev": true,
"requires": {
"comment-parser": "^1.1.5",
@@ -1755,118 +1758,6 @@
"schema-utils": "^2.7.0"
}
},
"@ngneat/transloco": {
"version": "2.20.1",
"resolved": "https://registry.npmjs.org/@ngneat/transloco/-/transloco-2.20.1.tgz",
"integrity": "sha512-V5NQvjrjAKWgYuQzC1FW+fisqM5gzci+WxkOQYYz7GaSIdo5Be8XvB6GM0QDFHPSGIBzBSg8UBAEzo0zeg7T/g==",
"requires": {
"@ngneat/transloco-utils": "^1.0.6",
"flat": "5.0.2",
"lodash.kebabcase": "^4.1.1",
"ora": "^3.4.0",
"replace-in-file": "^4.1.2",
"tslib": "^1.9.0"
},
"dependencies": {
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
},
"cli-cursor": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
"integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
"requires": {
"restore-cursor": "^2.0.0"
}
},
"log-symbols": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
"integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==",
"requires": {
"chalk": "^2.0.1"
}
},
"mimic-fn": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
"integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="
},
"onetime": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
"integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
"requires": {
"mimic-fn": "^1.0.0"
}
},
"ora": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/ora/-/ora-3.4.0.tgz",
"integrity": "sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==",
"requires": {
"chalk": "^2.4.2",
"cli-cursor": "^2.1.0",
"cli-spinners": "^2.0.0",
"log-symbols": "^2.2.0",
"strip-ansi": "^5.2.0",
"wcwidth": "^1.0.1"
}
},
"restore-cursor": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
"integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
"requires": {
"onetime": "^2.0.0",
"signal-exit": "^3.0.2"
}
},
"strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"requires": {
"ansi-regex": "^4.1.0"
}
},
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}
}
},
"@ngneat/transloco-utils": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@ngneat/transloco-utils/-/transloco-utils-1.1.2.tgz",
"integrity": "sha512-yljMAoRNr+qr1z+DTUlY98SWm/ldbssCNmI3tE+A1183ppYKcnpdfSJQKrx1rg4rmwNfyKyexlmZ6EubosqZ8A==",
"requires": {
"cosmiconfig": "6.0.0",
"tslib": "^1.9.0"
},
"dependencies": {
"cosmiconfig": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
"integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
"requires": {
"@types/parse-json": "^4.0.0",
"import-fresh": "^3.1.0",
"parse-json": "^5.0.0",
"path-type": "^4.0.0",
"yaml": "^1.7.2"
}
},
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}
}
},
"@ngtools/webpack": {
"version": "12.0.0",
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-12.0.0.tgz",
@@ -2152,7 +2043,8 @@
"@types/parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
"dev": true
},
"@types/q": {
"version": "0.0.32",
@@ -2192,76 +2084,19 @@
}
},
"@typescript-eslint/eslint-plugin": {
"version": "4.24.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.24.0.tgz",
"integrity": "sha512-qbCgkPM7DWTsYQGjx9RTuQGswi+bEt0isqDBeo+CKV0953zqI0Tp7CZ7Fi9ipgFA6mcQqF4NOVNwS/f2r6xShw==",
"version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.23.0.tgz",
"integrity": "sha512-tGK1y3KIvdsQEEgq6xNn1DjiFJtl+wn8JJQiETtCbdQxw1vzjXyAaIkEmO2l6Nq24iy3uZBMFQjZ6ECf1QdgGw==",
"dev": true,
"requires": {
"@typescript-eslint/experimental-utils": "4.24.0",
"@typescript-eslint/scope-manager": "4.24.0",
"@typescript-eslint/experimental-utils": "4.23.0",
"@typescript-eslint/scope-manager": "4.23.0",
"debug": "^4.1.1",
"functional-red-black-tree": "^1.0.1",
"lodash": "^4.17.15",
"regexpp": "^3.0.0",
"semver": "^7.3.2",
"tsutils": "^3.17.1"
},
"dependencies": {
"@typescript-eslint/experimental-utils": {
"version": "4.24.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.24.0.tgz",
"integrity": "sha512-IwTT2VNDKH1h8RZseMH4CcYBz6lTvRoOLDuuqNZZoThvfHEhOiZPQCow+5El3PtyxJ1iDr6UXZwYtE3yZQjhcw==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.3",
"@typescript-eslint/scope-manager": "4.24.0",
"@typescript-eslint/types": "4.24.0",
"@typescript-eslint/typescript-estree": "4.24.0",
"eslint-scope": "^5.0.0",
"eslint-utils": "^2.0.0"
}
},
"@typescript-eslint/scope-manager": {
"version": "4.24.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.24.0.tgz",
"integrity": "sha512-9+WYJGDnuC9VtYLqBhcSuM7du75fyCS/ypC8c5g7Sdw7pGL4NDTbeH38eJPfzIydCHZDoOgjloxSAA3+4l/zsA==",
"dev": true,
"requires": {
"@typescript-eslint/types": "4.24.0",
"@typescript-eslint/visitor-keys": "4.24.0"
}
},
"@typescript-eslint/types": {
"version": "4.24.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.24.0.tgz",
"integrity": "sha512-tkZUBgDQKdvfs8L47LaqxojKDE+mIUmOzdz7r+u+U54l3GDkTpEbQ1Jp3cNqqAU9vMUCBA1fitsIhm7yN0vx9Q==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "4.24.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.24.0.tgz",
"integrity": "sha512-kBDitL/by/HK7g8CYLT7aKpAwlR8doshfWz8d71j97n5kUa5caHWvY0RvEUEanL/EqBJoANev8Xc/mQ6LLwXGA==",
"dev": true,
"requires": {
"@typescript-eslint/types": "4.24.0",
"@typescript-eslint/visitor-keys": "4.24.0",
"debug": "^4.1.1",
"globby": "^11.0.1",
"is-glob": "^4.0.1",
"semver": "^7.3.2",
"tsutils": "^3.17.1"
}
},
"@typescript-eslint/visitor-keys": {
"version": "4.24.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.24.0.tgz",
"integrity": "sha512-4ox1sjmGHIxjEDBnMCtWFFhErXtKA1Ec0sBpuz0fqf3P+g3JFGyTxxbF06byw0FRsPnnbq44cKivH7Ks1/0s6g==",
"dev": true,
"requires": {
"@typescript-eslint/types": "4.24.0",
"eslint-visitor-keys": "^2.0.0"
}
}
}
},
"@typescript-eslint/experimental-utils": {
@@ -2279,58 +2114,15 @@
}
},
"@typescript-eslint/parser": {
"version": "4.24.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.24.0.tgz",
"integrity": "sha512-dj1ZIh/4QKeECLb2f/QjRwMmDArcwc2WorWPRlB8UNTZlY1KpTVsbX7e3ZZdphfRw29aTFUSNuGB8w9X5sS97w==",
"version": "4.23.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.23.0.tgz",
"integrity": "sha512-wsvjksHBMOqySy/Pi2Q6UuIuHYbgAMwLczRl4YanEPKW5KVxI9ZzDYh3B5DtcZPQTGRWFJrfcbJ6L01Leybwug==",
"dev": true,
"requires": {
"@typescript-eslint/scope-manager": "4.24.0",
"@typescript-eslint/types": "4.24.0",
"@typescript-eslint/typescript-estree": "4.24.0",
"@typescript-eslint/scope-manager": "4.23.0",
"@typescript-eslint/types": "4.23.0",
"@typescript-eslint/typescript-estree": "4.23.0",
"debug": "^4.1.1"
},
"dependencies": {
"@typescript-eslint/scope-manager": {
"version": "4.24.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.24.0.tgz",
"integrity": "sha512-9+WYJGDnuC9VtYLqBhcSuM7du75fyCS/ypC8c5g7Sdw7pGL4NDTbeH38eJPfzIydCHZDoOgjloxSAA3+4l/zsA==",
"dev": true,
"requires": {
"@typescript-eslint/types": "4.24.0",
"@typescript-eslint/visitor-keys": "4.24.0"
}
},
"@typescript-eslint/types": {
"version": "4.24.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.24.0.tgz",
"integrity": "sha512-tkZUBgDQKdvfs8L47LaqxojKDE+mIUmOzdz7r+u+U54l3GDkTpEbQ1Jp3cNqqAU9vMUCBA1fitsIhm7yN0vx9Q==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "4.24.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.24.0.tgz",
"integrity": "sha512-kBDitL/by/HK7g8CYLT7aKpAwlR8doshfWz8d71j97n5kUa5caHWvY0RvEUEanL/EqBJoANev8Xc/mQ6LLwXGA==",
"dev": true,
"requires": {
"@typescript-eslint/types": "4.24.0",
"@typescript-eslint/visitor-keys": "4.24.0",
"debug": "^4.1.1",
"globby": "^11.0.1",
"is-glob": "^4.0.1",
"semver": "^7.3.2",
"tsutils": "^3.17.1"
}
},
"@typescript-eslint/visitor-keys": {
"version": "4.24.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.24.0.tgz",
"integrity": "sha512-4ox1sjmGHIxjEDBnMCtWFFhErXtKA1Ec0sBpuz0fqf3P+g3JFGyTxxbF06byw0FRsPnnbq44cKivH7Ks1/0s6g==",
"dev": true,
"requires": {
"@typescript-eslint/types": "4.24.0",
"eslint-visitor-keys": "^2.0.0"
}
}
}
},
"@typescript-eslint/scope-manager": {
@@ -2706,12 +2498,14 @@
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
"dev": true
},
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"requires": {
"color-convert": "^1.9.0"
}
@@ -2727,9 +2521,9 @@
}
},
"apexcharts": {
"version": "3.26.3",
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.26.3.tgz",
"integrity": "sha512-zbP7RBBV2CGffoVMIuTCUG64YbEUzV8IIT7iNVLMtY/OAVXTjPksDxSqKIniTvgJoscKe6sx4P56qDpBSU19VA==",
"version": "3.26.2",
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.26.2.tgz",
"integrity": "sha512-CD7bad4ygwc9rs9vOQDDagUcoJ1mcc9BwNSiQB14l6jiZBCQKrXxnG4I1ZjJ2MIel/Y5GmsJFs8HTcZBqpe/Ew==",
"requires": {
"svg.draggable.js": "^2.2.2",
"svg.easing.js": "^2.0.0",
@@ -3054,7 +2848,8 @@
"balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
"base": {
"version": "0.11.2",
@@ -3251,6 +3046,7 @@
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -3405,7 +3201,8 @@
"callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"dev": true
},
"camelcase": {
"version": "6.2.0",
@@ -3453,6 +3250,7 @@
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
@@ -3488,9 +3286,9 @@
"dev": true
},
"chroma-js": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-2.1.2.tgz",
"integrity": "sha512-ri/ouYDWuxfus3UcaMxC1Tfp3IE9K5iQzxc2hSxbBRVNQFut1UuGAsZmiAf2mOUubzGJwgMSv9lHg+XqLaz1QQ==",
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-2.1.1.tgz",
"integrity": "sha512-gYc5/Dooshun2OikK7oY/hYnoEiZ0dxqRpXosEdYRYm505vU5mRsHFqIW062C9nMtr32DVErP6mlxuepo2kNkw==",
"dev": true,
"requires": {
"cross-env": "^6.0.3"
@@ -3549,7 +3347,8 @@
"cli-spinners": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.0.tgz",
"integrity": "sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q=="
"integrity": "sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==",
"dev": true
},
"cli-width": {
"version": "3.0.0",
@@ -3665,6 +3464,7 @@
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"requires": {
"color-name": "1.1.3"
}
@@ -3672,7 +3472,8 @@
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"dev": true
},
"color-string": {
"version": "1.5.5",
@@ -3772,7 +3573,8 @@
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true
},
"connect": {
"version": "3.7.0",
@@ -4555,7 +4357,8 @@
"decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
"dev": true
},
"decode-uri-component": {
"version": "0.2.0",
@@ -4596,6 +4399,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz",
"integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=",
"dev": true,
"requires": {
"clone": "^1.0.2"
},
@@ -4603,7 +4407,8 @@
"clone": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
"integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4="
"integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=",
"dev": true
}
}
},
@@ -4919,15 +4724,16 @@
"dev": true
},
"electron-to-chromium": {
"version": "1.3.727",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.727.tgz",
"integrity": "sha512-Mfz4FIB4FSvEwBpDfdipRIrwd6uo8gUDoRDF4QEYb4h4tSuI3ov594OrjU6on042UlFHouIJpClDODGkPcBSbg==",
"version": "1.3.728",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.728.tgz",
"integrity": "sha512-SHv4ziXruBpb1Nz4aTuqEHBYi/9GNCJMYIJgDEXrp/2V01nFXMNFUTli5Z85f5ivSkioLilQatqBYFB44wNJrA==",
"dev": true
},
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
},
"emoji-toolkit": {
"version": "6.5.1",
@@ -5071,6 +4877,7 @@
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
"dev": true,
"requires": {
"is-arrayish": "^0.2.1"
}
@@ -5146,7 +4953,8 @@
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"dev": true
},
"eslint": {
"version": "7.26.0",
@@ -5440,9 +5248,9 @@
}
},
"eslint-plugin-import": {
"version": "2.23.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.23.2.tgz",
"integrity": "sha512-LmNoRptHBxOP+nb0PIKz1y6OSzCJlB+0g0IGS3XV4KaKk2q4szqQ6s6F1utVf5ZRkxk/QOTjdxe7v4VjS99Bsg==",
"version": "2.23.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.23.0.tgz",
"integrity": "sha512-W5v1f7Cbam6BwbSSKtN6+yoUAms07ozbqAEhLOtf5erEqRv7w67PeZuAuBThaUkzdq2GfBx/5GBvxF+NWJshrQ==",
"dev": true,
"requires": {
"array-includes": "^3.1.3",
@@ -5539,12 +5347,12 @@
}
},
"eslint-plugin-jsdoc": {
"version": "34.8.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-34.8.2.tgz",
"integrity": "sha512-UOU9A40Cl806JMtla2vF+RM6sNqfLPbhLv9FZqhcC7+LmChD3DVaWqM7ADxpF0kMyZNWe1QKUnqGnXaA3NTn+w==",
"version": "34.2.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-34.2.2.tgz",
"integrity": "sha512-9JCeUUZ2MefGd/Pl3blx9/EDjCd7/Z2+fcVBuJdNdvSLApRqZ8CwjGjLHf5s4uVikqF968A0CfnID0WK9B/H/w==",
"dev": true,
"requires": {
"@es-joy/jsdoccomment": "^0.6.0",
"@es-joy/jsdoccomment": "^0.4.4",
"comment-parser": "1.1.5",
"debug": "^4.3.1",
"esquery": "^1.4.0",
@@ -6072,16 +5880,12 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"dev": true,
"requires": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
}
},
"flat": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
"integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ=="
},
"flat-cache": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
@@ -6189,7 +5993,8 @@
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true
},
"fsevents": {
"version": "2.3.2",
@@ -6271,7 +6076,8 @@
"get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true
},
"get-intrinsic": {
"version": "1.1.1",
@@ -6311,6 +6117,7 @@
"version": "7.1.7",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
"integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -6482,7 +6289,8 @@
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true
},
"has-symbols": {
"version": "1.0.2",
@@ -6904,6 +6712,7 @@
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
"dev": true,
"requires": {
"parent-module": "^1.0.0",
"resolve-from": "^4.0.0"
@@ -6992,6 +6801,7 @@
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"dev": true,
"requires": {
"once": "^1.3.0",
"wrappy": "1"
@@ -7000,7 +6810,8 @@
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
"ini": {
"version": "2.0.0",
@@ -7145,7 +6956,8 @@
"is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
"dev": true
},
"is-bigint": {
"version": "1.0.2",
@@ -7285,7 +7097,8 @@
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true
},
"is-glob": {
"version": "4.0.1",
@@ -7618,7 +7431,8 @@
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true
},
"js-yaml": {
"version": "3.14.1",
@@ -7657,7 +7471,8 @@
"json-parse-even-better-errors": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
"dev": true
},
"json-schema": {
"version": "0.2.3",
@@ -8061,7 +7876,8 @@
"lines-and-columns": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
"integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA="
"integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=",
"dev": true
},
"load-json-file": {
"version": "4.0.0",
@@ -8114,6 +7930,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"dev": true,
"requires": {
"p-locate": "^4.1.0"
}
@@ -8153,11 +7970,6 @@
"integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=",
"dev": true
},
"lodash.kebabcase": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz",
"integrity": "sha1-hImxyw0p/4gZXM7KRI/21swpXDY="
},
"lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@@ -8371,9 +8183,9 @@
}
},
"marked": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/marked/-/marked-2.0.4.tgz",
"integrity": "sha512-MIL0xKRDQM3DE7dJr/wa6JV0EmK9yZ3cwuTc2bu66FNm/tmEMm9cJCgJZpt9R+K1T+pB2iBNV55wvnwSd345zg=="
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/marked/-/marked-2.0.3.tgz",
"integrity": "sha512-5otztIIcJfPc2qGTN8cVtOJEjNJZ0jwa46INMagrYfk0EvqtRuEHLsEe0LrFS0/q+ZRKT0+kXK7P2T1AN5lWRA=="
},
"mdn-data": {
"version": "2.0.14",
@@ -8580,6 +8392,7 @@
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@@ -8808,9 +8621,9 @@
"dev": true
},
"ng-apexcharts": {
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/ng-apexcharts/-/ng-apexcharts-1.5.10.tgz",
"integrity": "sha512-NcAQZnzG4Knf9QG8PP20B9lx+rLm1ynoKIP3tMDsv272VAMaN0Yn01B50WR7yI3ouWufuH4qOPB8lqjjyqbJJg==",
"version": "1.5.9",
"resolved": "https://registry.npmjs.org/ng-apexcharts/-/ng-apexcharts-1.5.9.tgz",
"integrity": "sha512-ZRsU9meBvXNPG1vhEolCJGRrhfR7Nnj37E6H5CHfWpUEAw9/89U5TK+yDD+myPhT0ubQbRb6fZJoG74dEebXYA==",
"requires": {
"tslib": "^1.10.0"
},
@@ -8823,16 +8636,16 @@
}
},
"ngx-markdown": {
"version": "12.0.0",
"resolved": "https://registry.npmjs.org/ngx-markdown/-/ngx-markdown-12.0.0.tgz",
"integrity": "sha512-U8EeHNGVxdjn9QDsPZy6h05I39QRiBV7a5n95NDBP1nACRos31sPzVa3+NMLs0Z6HzFpnHuIR4759uveRn7KBg==",
"version": "11.1.3",
"resolved": "https://registry.npmjs.org/ngx-markdown/-/ngx-markdown-11.1.3.tgz",
"integrity": "sha512-z32q8l76ubrcP62L03mdvrizwueLBHV10LkT8MEDnFcjmY+8J1PytxFJ9EBTJpvc+CaPolgAoi7felN2XJZTSg==",
"requires": {
"@types/marked": "^2.0.0",
"emoji-toolkit": "^6.5.0",
"emoji-toolkit": "^6.0.1",
"katex": "^0.13.0",
"marked": "^2.0.0",
"prismjs": "^1.23.0",
"tslib": "^2.1.0"
"tslib": "^2.0.0"
}
},
"ngx-quill": {
@@ -9204,6 +9017,7 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true,
"requires": {
"wrappy": "1"
}
@@ -9358,6 +9172,7 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
}
@@ -9366,6 +9181,7 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"dev": true,
"requires": {
"p-limit": "^2.2.0"
}
@@ -9391,7 +9207,8 @@
"p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true
},
"pacote": {
"version": "11.3.2",
@@ -9435,6 +9252,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
"dev": true,
"requires": {
"callsites": "^3.0.0"
}
@@ -9472,6 +9290,7 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"error-ex": "^1.3.1",
@@ -9564,12 +9383,14 @@
"path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"dev": true
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true
},
"path-is-inside": {
"version": "1.0.2",
@@ -9618,7 +9439,8 @@
"path-type": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
"dev": true
},
"perfect-scrollbar": {
"version": "1.5.1",
@@ -9765,14 +9587,22 @@
"dev": true
},
"postcss": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.0.tgz",
"integrity": "sha512-+ogXpdAjWGa+fdYY5BQ96V/6tAo+TdSSIMP5huJBIygdWwKtVoB5JWZ7yUd4xZ8r+8Kvvx4nyg/PQ071H4UtcQ==",
"version": "8.2.15",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.15.tgz",
"integrity": "sha512-2zO3b26eJD/8rb106Qu2o7Qgg52ND5HPjcyQiK2B98O388h43A448LCslC0dI2P97wCAQRJsFvwTRcXxTKds+Q==",
"dev": true,
"requires": {
"colorette": "^1.2.2",
"nanoid": "^3.1.23",
"source-map-js": "^0.6.2"
"source-map": "^0.6.1"
},
"dependencies": {
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
}
}
},
"postcss-attribute-case-insensitive": {
@@ -12246,91 +12076,6 @@
"integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
"dev": true
},
"replace-in-file": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/replace-in-file/-/replace-in-file-4.3.1.tgz",
"integrity": "sha512-FqVvfmpqGTD2JRGI1JjJ86b24P17x/WWwGdxExeyJxnh/2rVQz2+jXfD1507UnnhEQw092X0u0DPCBf1WC4ooQ==",
"requires": {
"chalk": "^2.4.2",
"glob": "^7.1.6",
"yargs": "^15.0.2"
},
"dependencies": {
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"requires": {
"color-convert": "^2.0.1"
}
},
"camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
},
"cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
"requires": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^6.2.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
"requires": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
}
},
"yargs": {
"version": "15.4.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
"requires": {
"cliui": "^6.0.0",
"decamelize": "^1.2.0",
"find-up": "^4.1.0",
"get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^4.2.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^18.1.2"
}
},
"yargs-parser": {
"version": "18.1.3",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
}
}
},
"request": {
"version": "2.88.2",
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
@@ -12370,7 +12115,8 @@
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
"dev": true
},
"require-from-string": {
"version": "2.0.2",
@@ -12381,7 +12127,8 @@
"require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
"dev": true
},
"requires-port": {
"version": "1.0.0",
@@ -12419,7 +12166,8 @@
"resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true
},
"resolve-url": {
"version": "0.2.1",
@@ -12873,7 +12621,8 @@
"set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
"dev": true
},
"set-immediate-shim": {
"version": "1.0.1",
@@ -12937,7 +12686,8 @@
"signal-exit": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
"integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
"integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==",
"dev": true
},
"simple-swizzle": {
"version": "0.2.2",
@@ -13344,9 +13094,9 @@
}
},
"spdx-license-ids": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.8.tgz",
"integrity": "sha512-NDgA96EnaLSvtbM7trJj+t1LUR3pirkDCcz9nOUlPb5DMBGsH7oES6C3hs3j7R9oHEa1EMvReS/BUAIT5Tcr0g==",
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz",
"integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==",
"dev": true
},
"spdy": {
@@ -13473,6 +13223,7 @@
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
"integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
"dev": true,
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
@@ -13520,6 +13271,7 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"dev": true,
"requires": {
"ansi-regex": "^5.0.0"
}
@@ -13647,6 +13399,7 @@
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
@@ -14512,6 +14265,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
"integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=",
"dev": true,
"requires": {
"defaults": "^1.0.3"
}
@@ -15140,7 +14894,8 @@
"which-module": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
"dev": true
},
"wide-align": {
"version": "1.1.3",
@@ -15250,7 +15005,8 @@
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true
},
"ws": {
"version": "6.2.1",
@@ -15286,7 +15042,8 @@
"y18n": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
"dev": true
},
"yallist": {
"version": "4.0.0",
@@ -15297,7 +15054,8 @@
"yaml": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
"dev": true
},
"yargs": {
"version": "13.3.2",

View File

@@ -1,6 +1,6 @@
{
"name": "@fuse/demo",
"version": "13.0.1",
"version": "13.0.0",
"license": "https://themeforest.net/licenses/standard",
"private": true,
"scripts": {
@@ -31,14 +31,13 @@
"@fullcalendar/moment": "4.4.2",
"@fullcalendar/rrule": "4.4.2",
"@fullcalendar/timegrid": "4.4.2",
"@ngneat/transloco": "2.20.1",
"apexcharts": "3.26.3",
"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.10",
"ngx-markdown": "12.0.0",
"ng-apexcharts": "1.5.9",
"ngx-markdown": "11.1.3",
"ngx-quill": "14.0.0",
"perfect-scrollbar": "1.5.1",
"quill": "1.3.7",
@@ -68,13 +67,13 @@
"@types/lodash": "4.14.169",
"@types/lodash-es": "4.17.4",
"@types/node": "12.20.13",
"@typescript-eslint/eslint-plugin": "4.24.0",
"@typescript-eslint/parser": "4.24.0",
"@typescript-eslint/eslint-plugin": "4.23.0",
"@typescript-eslint/parser": "4.23.0",
"autoprefixer": "10.2.5",
"chroma-js": "2.1.2",
"chroma-js": "2.1.1",
"eslint": "7.26.0",
"eslint-plugin-import": "2.23.2",
"eslint-plugin-jsdoc": "34.8.2",
"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",
@@ -84,7 +83,7 @@
"karma-jasmine": "4.0.1",
"karma-jasmine-html-reporter": "1.6.0",
"lodash": "4.17.21",
"postcss": "8.3.0",
"postcss": "8.2.15",
"protractor": "7.0.0",
"tailwindcss": "2.1.2",
"typescript": "4.2.4"

View File

@@ -1 +1 @@
export * from '@fuse/animations/public-api';
export * from './public-api';

View File

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

View File

@@ -61,9 +61,9 @@ export class FuseDateRangeComponent implements ControlValueAccessor, OnInit, OnD
private _viewContainerRef: ViewContainerRef
)
{
this._onChange = (): void => {
this._onChange = () => {
};
this._onTouched = (): void => {
this._onTouched = () => {
};
this.dateFormat = 'DD/MM/YYYY';
this.timeFormat = '12';
@@ -361,7 +361,7 @@ export class FuseDateRangeComponent implements ControlValueAccessor, OnInit, OnD
this._unsubscribeAll.complete();
// @ TODO: Workaround until "angular/issues/20007" resolved
this.writeValue = (): void => {
this.writeValue = () => {
};
}

View File

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

View File

@@ -11,7 +11,7 @@
[ngClass]="{'fuse-horizontal-navigation-item-active-forced': item.active}"
[routerLink]="[item.link]"
[routerLinkActive]="'fuse-horizontal-navigation-item-active'"
[routerLinkActiveOptions]="isActiveMatchOptions">
[routerLinkActiveOptions]="{exact: item.exactMatch || false}">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
@@ -39,7 +39,7 @@
[ngClass]="{'fuse-horizontal-navigation-item-active-forced': item.active}"
[routerLink]="[item.link]"
[routerLinkActive]="'fuse-horizontal-navigation-item-active'"
[routerLinkActiveOptions]="isActiveMatchOptions"
[routerLinkActiveOptions]="{exact: item.exactMatch || false}"
(click)="item.function(item)">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>

View File

@@ -1,11 +1,9 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
import { IsActiveMatchOptions } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseHorizontalNavigationComponent } from '@fuse/components/navigation/horizontal/horizontal.component';
import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types';
import { FuseUtilsService } from '@fuse/services/utils/utils.service';
@Component({
selector : 'fuse-horizontal-navigation-basic-item',
@@ -18,7 +16,6 @@ export class FuseHorizontalNavigationBasicItemComponent implements OnInit, OnDes
@Input() item: FuseNavigationItem;
@Input() name: string;
isActiveMatchOptions: IsActiveMatchOptions;
private _fuseHorizontalNavigationComponent: FuseHorizontalNavigationComponent;
private _unsubscribeAll: Subject<any> = new Subject<any>();
@@ -27,15 +24,9 @@ export class FuseHorizontalNavigationBasicItemComponent implements OnInit, OnDes
*/
constructor(
private _changeDetectorRef: ChangeDetectorRef,
private _fuseNavigationService: FuseNavigationService,
private _fuseUtilsService: FuseUtilsService
private _fuseNavigationService: FuseNavigationService
)
{
// Set the equivalent of {exact: false} as default for active match options.
// We are not assigning the item.isActiveMatchOptions directly to the
// [routerLinkActiveOptions] because if it's "undefined" initially, the router
// will throw an error and stop working.
this.isActiveMatchOptions = this._fuseUtilsService.subsetMatchOptions;
}
// -----------------------------------------------------------------------------------------------------
@@ -47,20 +38,9 @@ export class FuseHorizontalNavigationBasicItemComponent implements OnInit, OnDes
*/
ngOnInit(): void
{
// Set the "isActiveMatchOptions" either from item's
// "isActiveMatchOptions" or the equivalent form of
// item's "exactMatch" option
this.isActiveMatchOptions =
this.item.isActiveMatchOptions ?? this.item.exactMatch
? this._fuseUtilsService.exactMatchOptions
: this._fuseUtilsService.subsetMatchOptions;
// Get the parent navigation component
this._fuseHorizontalNavigationComponent = this._fuseNavigationService.getComponent(this.name);
// Mark for check
this._changeDetectorRef.markForCheck();
// Subscribe to onRefreshed on the navigation component
this._fuseHorizontalNavigationComponent.onRefreshed.pipe(
takeUntil(this._unsubscribeAll)

View File

@@ -46,7 +46,7 @@ export class FuseNavigationService
*
* @param name
*/
getComponent<T>(name: string): T
getComponent(name: string): any
{
return this._componentRegistry.get(name);
}

View File

@@ -1,5 +1,3 @@
import { IsActiveMatchOptions } from '@angular/router';
export interface FuseNavigationItem
{
id?: string;
@@ -18,7 +16,6 @@ export interface FuseNavigationItem
link?: string;
externalLink?: boolean;
exactMatch?: boolean;
isActiveMatchOptions?: IsActiveMatchOptions;
function?: (item: FuseNavigationItem) => void;
classes?: {
title?: string;

View File

@@ -11,7 +11,7 @@
[ngClass]="{'fuse-vertical-navigation-item-active-forced': item.active}"
[routerLink]="[item.link]"
[routerLinkActive]="'fuse-vertical-navigation-item-active'"
[routerLinkActiveOptions]="isActiveMatchOptions">
[routerLinkActiveOptions]="{exact: item.exactMatch || false}">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</a>
@@ -39,7 +39,7 @@
[ngClass]="{'fuse-vertical-navigation-item-active-forced': item.active}"
[routerLink]="[item.link]"
[routerLinkActive]="'fuse-vertical-navigation-item-active'"
[routerLinkActiveOptions]="isActiveMatchOptions"
[routerLinkActiveOptions]="{exact: item.exactMatch || false}"
(click)="item.function(item)">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</a>

View File

@@ -1,5 +1,4 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
import { IsActiveMatchOptions } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseVerticalNavigationComponent } from '@fuse/components/navigation/vertical/vertical.component';
@@ -18,7 +17,6 @@ export class FuseVerticalNavigationBasicItemComponent implements OnInit, OnDestr
@Input() item: FuseNavigationItem;
@Input() name: string;
isActiveMatchOptions: IsActiveMatchOptions;
private _fuseVerticalNavigationComponent: FuseVerticalNavigationComponent;
private _unsubscribeAll: Subject<any> = new Subject<any>();
@@ -31,11 +29,6 @@ export class FuseVerticalNavigationBasicItemComponent implements OnInit, OnDestr
private _fuseUtilsService: FuseUtilsService
)
{
// Set the equivalent of {exact: false} as default for active match options.
// We are not assigning the item.isActiveMatchOptions directly to the
// [routerLinkActiveOptions] because if it's "undefined" initially, the router
// will throw an error and stop working.
this.isActiveMatchOptions = this._fuseUtilsService.subsetMatchOptions;
}
// -----------------------------------------------------------------------------------------------------
@@ -47,20 +40,9 @@ export class FuseVerticalNavigationBasicItemComponent implements OnInit, OnDestr
*/
ngOnInit(): void
{
// Set the "isActiveMatchOptions" either from item's
// "isActiveMatchOptions" or the equivalent form of
// item's "exactMatch" option
this.isActiveMatchOptions =
this.item.isActiveMatchOptions ?? this.item.exactMatch
? this._fuseUtilsService.exactMatchOptions
: this._fuseUtilsService.subsetMatchOptions;
// Get the parent navigation component
this._fuseVerticalNavigationComponent = this._fuseNavigationService.getComponent(this.name);
// Mark for check
this._changeDetectorRef.markForCheck();
// Subscribe to onRefreshed on the navigation component
this._fuseVerticalNavigationComponent.onRefreshed.pipe(
takeUntil(this._unsubscribeAll)

View File

@@ -73,10 +73,10 @@ export class FuseVerticalNavigationComponent implements OnChanges, OnInit, After
private _fuseUtilsService: FuseUtilsService
)
{
this._handleAsideOverlayClick = (): void => {
this._handleAsideOverlayClick = () => {
this.closeAside();
};
this._handleOverlayClick = (): void => {
this._handleOverlayClick = () => {
this.close();
};
}

View File

@@ -389,7 +389,7 @@ export class FuseScrollbarDirective implements OnChanges, OnInit, OnDestroy
const cosParameter = (oldValue - value) / 2;
const step = (newTimestamp: number): void => {
const step = (newTimestamp: number) => {
scrollCount += Math.PI / (speed / (newTimestamp - oldTimestamp));
newValue = Math.round(value + cosParameter + cosParameter * Math.cos(scrollCount));

View File

@@ -51,7 +51,7 @@ export class FuseMockApiInterceptor implements HttpInterceptor
delay(handler.delay ?? this._defaultDelay ?? 0),
switchMap((response) => {
// If there is no response data,
// If there is no response mock-api,
// throw an error response
if ( !response )
{
@@ -64,7 +64,7 @@ export class FuseMockApiInterceptor implements HttpInterceptor
return throwError(response);
}
// Parse the response data
// Parse the response mock-api
const data = {
status: response[0],
body : response[1]

View File

@@ -29,7 +29,7 @@ export class FuseMockApiModule
{
provide : APP_INITIALIZER,
deps : [...mockApiServices],
useFactory: () => (): any => null,
useFactory: () => () => null,
multi : true
},
{

View File

@@ -1 +1 @@
export * from '@fuse/services/tailwind/public-api';
export * from '@fuse/services/media-watcher/public-api';

View File

@@ -1,2 +1,2 @@
export * from '@fuse/services/tailwind/tailwind.module';
export * from '@fuse/services/tailwind/tailwind.service';
export * from '@fuse/services/media-watcher/media-watcher.module';
export * from '@fuse/services/media-watcher/media-watcher.service';

View File

@@ -1 +1 @@
export * from '@fuse/services/utils/public-api';
export * from '@fuse/services/config/public-api';

View File

@@ -1,5 +1,4 @@
import { Injectable } from '@angular/core';
import { IsActiveMatchOptions } from '@angular/router';
@Injectable({
providedIn: 'root'
@@ -13,36 +12,6 @@ export class FuseUtilsService
{
}
// -----------------------------------------------------------------------------------------------------
// @ Accessors
// -----------------------------------------------------------------------------------------------------
/**
* Get the equivalent "IsActiveMatchOptions" options for "exact = true".
*/
get exactMatchOptions(): IsActiveMatchOptions
{
return {
paths : 'exact',
fragment : 'ignored',
matrixParams: 'ignored',
queryParams : 'exact'
};
}
/**
* Get the equivalent "IsActiveMatchOptions" options for "exact = false".
*/
get subsetMatchOptions(): IsActiveMatchOptions
{
return {
paths : 'subset',
fragment : 'ignored',
matrixParams: 'ignored',
queryParams : 'subset'
};
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------

View File

@@ -178,7 +178,11 @@
/* Add hover and focus style on all buttons */
.mat-button-focus-overlay {
@apply bg-gray-400 bg-opacity-20 dark:bg-black dark:bg-opacity-5 #{'!important'};
@apply bg-gray-400 bg-opacity-20 #{'!important'};
.dark & {
background-color: rgba(0, 0, 0, 0.05) !important;
}
}
/* On palette colored buttons, use a darker color */
@@ -249,7 +253,11 @@
/* Add hover and focus styles */
.mat-button-focus-overlay {
@apply bg-gray-400 bg-opacity-20 dark:bg-black dark:bg-opacity-5 #{'!important'};
@apply bg-gray-400 bg-opacity-20 #{'!important'};
.dark & {
background-color: rgba(0, 0, 0, 0.05) !important;
}
}
/* On primary colored buttons, use the primary color as focus overlay */
@@ -322,11 +330,19 @@
/* Border color */
&:not(.mat-button-disabled) {
@apply border-gray-300 dark:border-gray-500 #{'!important'};
@apply border-gray-300 #{'!important'};
.dark & {
@apply border-gray-500 #{'!important'};
}
}
&.mat-button-disabled {
@apply border-gray-200 dark:border-gray-600 #{'!important'};
@apply border-gray-200 #{'!important'};
.dark & {
@apply border-gray-600 #{'!important'};
}
}
}
@@ -526,7 +542,13 @@
border-radius: 6px;
padding: 0 16px;
border-width: 1px;
@apply shadow-sm bg-white border-gray-300 dark:bg-black dark:bg-opacity-5 dark:border-gray-500 #{'!important'};
background-color: white;
@apply border-gray-300 shadow-sm #{'!important'};
.dark & {
background-color: rgba(0, 0, 0, 0.05) !important;
@apply border-gray-500 #{'!important'};
}
.mat-form-field-prefix {
@@ -611,28 +633,12 @@
@apply icon-size-6;
}
/* Make mat-select usable as prefix and suffix */
/* Make mat-select usable as */
/* prefix and suffix */
.mat-select {
display: flex;
align-items: center;
&:focus {
.mat-select-trigger {
.mat-select-value {
@apply text-primary #{'!important'};
}
.mat-select-arrow-wrapper {
.mat-select-arrow {
border-top-color: var(--fuse-primary) !important;
}
}
}
}
.mat-select-trigger {
display: flex;
align-items: center;
@@ -657,7 +663,6 @@
.mat-select-arrow {
min-height: 0;
@apply text-gray-500 dark:text-gray-400 #{'!important'};
}
}
}
@@ -1027,7 +1032,11 @@
.mat-form-field-prefix,
.mat-form-field-suffix {
@apply bg-default border-gray-300 dark:border-gray-500 #{'!important'};
@apply border-gray-300 bg-default #{'!important'};
.dark & {
@apply border-gray-500 #{'!important'};
}
}
}
}

View File

@@ -1 +1 @@
export * from '@fuse/validators/public-api';
export * from './public-api';

View File

@@ -1 +1 @@
export * from '@fuse/validators/validators';
export * from './validators';

View File

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

View File

@@ -27,18 +27,18 @@ const routerConfig: ExtraOptions = {
BrowserAnimationsModule,
RouterModule.forRoot(appRoutes, routerConfig),
// Fuse, FuseConfig & FuseMockAPI
// Fuse & Fuse Mock API
FuseModule,
FuseConfigModule.forRoot(appConfig),
FuseMockApiModule.forRoot(mockApiServices),
// Core module of your application
// Core
CoreModule,
// Layout module of your application
// Layout
LayoutModule,
// 3rd party modules that require global configuration via forRoot
// 3rd party modules
MarkdownModule.forRoot({})
],
bootstrap : [

View File

@@ -5,19 +5,18 @@ import { LayoutComponent } from 'app/layout/layout.component';
import { InitialDataResolver } from 'app/app.resolvers';
// @formatter:off
/* eslint-disable max-len */
/* eslint-disable @typescript-eslint/explicit-function-return-type */
// tslint:disable:max-line-length
export const appRoutes: Route[] = [
// Redirect empty path to '/dashboards/project'
{path: '', pathMatch : 'full', redirectTo: 'dashboards/project'},
// Redirect empty path to '/example'
{path: '', pathMatch : 'full', redirectTo: 'example'},
// Redirect signed in user to the '/dashboards/project'
// Redirect signed in user to the '/example'
//
// After the user signs in, the sign in page will redirect the user to the 'signed-in-redirect'
// path. Below is another redirection for that path to redirect the user to the desired
// location. This is a small convenience to keep all main routes together here on this file.
{path: 'signed-in-redirect', pathMatch : 'full', redirectTo: 'dashboards/project'},
{path: 'signed-in-redirect', pathMatch : 'full', redirectTo: 'example'},
// Auth routes for guests
{
@@ -74,128 +73,7 @@ export const appRoutes: Route[] = [
initialData: InitialDataResolver,
},
children : [
// Dashboards
{path: 'dashboards', children: [
{path: 'project', loadChildren: () => import('app/modules/admin/dashboards/project/project.module').then(m => m.ProjectModule)},
{path: 'analytics', loadChildren: () => import('app/modules/admin/dashboards/analytics/analytics.module').then(m => m.AnalyticsModule)},
]},
// 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)},
]},
// Pages
{path: 'pages', children: [
// Activities
{path: 'activities', loadChildren: () => import('app/modules/admin/pages/activities/activities.module').then(m => m.ActivitiesModule)},
// Authentication
{path: 'authentication', loadChildren: () => import('app/modules/admin/pages/authentication/authentication.module').then(m => m.AuthenticationModule)},
// Coming soon
{path: 'coming-soon', loadChildren: () => import('app/modules/admin/pages/coming-soon/coming-soon.module').then(m => m.ComingSoonModule)},
// Error
{path: 'error', children: [
{path: '404', loadChildren: () => import('app/modules/admin/pages/error/error-404/error-404.module').then(m => m.Error404Module)},
{path: '500', loadChildren: () => import('app/modules/admin/pages/error/error-500/error-500.module').then(m => m.Error500Module)}
]},
// Invoice
{path: 'invoice', children: [
{path: 'printable', children: [
{path: 'compact', loadChildren: () => import('app/modules/admin/pages/invoice/printable/compact/compact.module').then(m => m.CompactModule)},
{path: 'modern', loadChildren: () => import('app/modules/admin/pages/invoice/printable/modern/modern.module').then(m => m.ModernModule)}
]}
]},
// Maintenance
{path: 'maintenance', loadChildren: () => import('app/modules/admin/pages/maintenance/maintenance.module').then(m => m.MaintenanceModule)},
// Pricing
{path: 'pricing', children: [
{path: 'modern', loadChildren: () => import('app/modules/admin/pages/pricing/modern/modern.module').then(m => m.PricingModernModule)},
{path: 'simple', loadChildren: () => import('app/modules/admin/pages/pricing/simple/simple.module').then(m => m.PricingSimpleModule)},
{path: 'single', loadChildren: () => import('app/modules/admin/pages/pricing/single/single.module').then(m => m.PricingSingleModule)},
{path: 'table', loadChildren: () => import('app/modules/admin/pages/pricing/table/table.module').then(m => m.PricingTableModule)}
]},
// 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
{path: 'ui', children: [
// Angular Material
{path: 'angular-material', loadChildren: () => import('app/modules/admin/ui/angular-material/angular-material.module').then(m => m.AngularMaterialModule)},
// TailwindCSS
{path: 'tailwindcss', loadChildren: () => import('app/modules/admin/ui/tailwindcss/tailwindcss.module').then(m => m.TailwindCSSModule)},
// Animations
{path: 'animations', loadChildren: () => import('app/modules/admin/ui/animations/animations.module').then(m => m.AnimationsModule)},
// Cards
{path: 'cards', loadChildren: () => import('app/modules/admin/ui/cards/cards.module').then(m => m.CardsModule)},
// Colors
{path: 'colors', loadChildren: () => import('app/modules/admin/ui/colors/colors.module').then(m => m.ColorsModule)},
// Datatable
{path: 'datatable', loadChildren: () => import('app/modules/admin/ui/datatable/datatable.module').then(m => m.DatatableModule)},
// Forms
{path: 'forms', children: [
{path: 'fields', loadChildren: () => import('app/modules/admin/ui/forms/fields/fields.module').then(m => m.FormsFieldsModule)},
{path: 'layouts', loadChildren: () => import('app/modules/admin/ui/forms/layouts/layouts.module').then(m => m.FormsLayoutsModule)},
{path: 'wizards', loadChildren: () => import('app/modules/admin/ui/forms/wizards/wizards.module').then(m => m.FormsWizardsModule)}
]},
// Icons
{path: 'icons', loadChildren: () => import('app/modules/admin/ui/icons/icons.module').then(m => m.IconsModule)},
// Page layouts
{path: 'page-layouts', loadChildren: () => import('app/modules/admin/ui/page-layouts/page-layouts.module').then(m => m.PageLayoutsModule)},
// Typography
{path: 'typography', loadChildren: () => import('app/modules/admin/ui/typography/typography.module').then(m => m.TypographyModule)}
]},
// Documentation
{path: 'docs', children: [
// Changelog
{path: 'changelog', loadChildren: () => import('app/modules/admin/docs/changelog/changelog.module').then(m => m.ChangelogModule)},
// Guides
{path: 'guides', loadChildren: () => import('app/modules/admin/docs/guides/guides.module').then(m => m.GuidesModule)},
// Core features
{path: 'core-features', loadChildren: () => import('app/modules/admin/docs/core-features/core-features.module').then(m => m.CoreFeaturesModule)},
// Other components
{path: 'other-components', loadChildren: () => import('app/modules/admin/docs/other-components/other-components.module').then(m => m.OtherComponentsModule)},
]},
// 404 & Catch all
{path: '404-not-found', pathMatch: 'full', loadChildren: () => import('app/modules/admin/pages/error/error-404/error-404.module').then(m => m.Error404Module)},
{path: '**', redirectTo: '404-not-found'}
{path: 'example', loadChildren: () => import('app/modules/admin/example/example.module').then(m => m.ExampleModule)},
]
}
];

View File

@@ -1,21 +0,0 @@
import { NgModule } from '@angular/core';
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { AuthService } from 'app/core/auth/auth.service';
import { AuthInterceptor } from 'app/core/auth/auth.interceptor';
@NgModule({
imports : [
HttpClientModule
],
providers: [
AuthService,
{
provide : HTTP_INTERCEPTORS,
useClass: AuthInterceptor,
multi : true
}
]
})
export class AuthModule
{
}

View File

@@ -1,13 +1,21 @@
import { NgModule, Optional, SkipSelf } from '@angular/core';
import { AuthModule } from 'app/core/auth/auth.module';
import { IconsModule } from 'app/core/icons/icons.module';
import { TranslocoCoreModule } from 'app/core/transloco/transloco.module';
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { DomSanitizer } from '@angular/platform-browser';
import { MatIconRegistry } from '@angular/material/icon';
import { AuthService } from 'app/core/auth/auth.service';
import { AuthInterceptor } from 'app/core/auth/auth.interceptor';
@NgModule({
imports: [
AuthModule,
IconsModule,
TranslocoCoreModule
imports : [
HttpClientModule
],
providers: [
AuthService,
{
provide : HTTP_INTERCEPTORS,
useClass: AuthInterceptor,
multi : true
}
]
})
export class CoreModule
@@ -16,6 +24,8 @@ export class CoreModule
* Constructor
*/
constructor(
private _domSanitizer: DomSanitizer,
private _matIconRegistry: MatIconRegistry,
@Optional() @SkipSelf() parentModule?: CoreModule
)
{
@@ -24,5 +34,14 @@ export class CoreModule
{
throw new Error('CoreModule has already been loaded. Import this module in the AppModule only.');
}
// 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'));
this._matIconRegistry.addSvgIconSetInNamespace('heroicons_solid', this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/heroicons-solid.svg'));
}
}

View File

@@ -1,25 +0,0 @@
import { NgModule } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { MatIconRegistry } from '@angular/material/icon';
@NgModule()
export class IconsModule
{
/**
* Constructor
*/
constructor(
private _domSanitizer: DomSanitizer,
private _matIconRegistry: MatIconRegistry
)
{
// 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'));
this._matIconRegistry.addSvgIconSetInNamespace('heroicons_solid', this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/heroicons-solid.svg'));
}
}

View File

@@ -1,32 +0,0 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Translation, TranslocoLoader } from '@ngneat/transloco';
@Injectable({
providedIn: 'root'
})
export class TranslocoHttpLoader implements TranslocoLoader
{
/**
* Constructor
*/
constructor(
private _httpClient: HttpClient)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Get translation
*
* @param lang
*/
getTranslation(lang: string): Observable<Translation>
{
return this._httpClient.get<Translation>(`/assets/i18n/${lang}.json`);
}
}

View File

@@ -1,50 +0,0 @@
import { Translation, TRANSLOCO_CONFIG, TRANSLOCO_LOADER, translocoConfig, TranslocoModule, TranslocoService } from '@ngneat/transloco';
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { environment } from 'environments/environment';
import { TranslocoHttpLoader } from 'app/core/transloco/transloco.http-loader';
@NgModule({
exports : [
TranslocoModule
],
providers: [
{
// Provide the default Transloco configuration
provide : TRANSLOCO_CONFIG,
useValue: translocoConfig({
availableLangs : [
{
id : 'en',
label: 'English'
},
{
id : 'tr',
label: 'Turkish'
}
],
defaultLang : 'en',
reRenderOnLangChange: true,
prodMode : environment.production
})
},
{
// Provide the default Transloco loader
provide : TRANSLOCO_LOADER,
useClass: TranslocoHttpLoader
},
{
// Preload the default language before the app starts to prevent empty/jumping content
provide : APP_INITIALIZER,
deps : [TranslocoService],
useFactory: (translocoService: TranslocoService): any => (): Promise<Translation> => {
const defaultLang = translocoService.getDefaultLang();
translocoService.setActiveLang(defaultLang);
return translocoService.load(defaultLang).toPromise();
},
multi : true
}
]
})
export class TranslocoCoreModule
{
}

View File

@@ -1,35 +0,0 @@
<!-- Button -->
<button
mat-icon-button
[matMenuTriggerFor]="languages">
<ng-container *ngTemplateOutlet="flagImage; context: {$implicit: activeLang}"></ng-container>
</button>
<!-- Language menu -->
<mat-menu
[xPosition]="'before'"
#languages="matMenu">
<ng-container *ngFor="let lang of availableLangs; trackBy: trackByFn">
<button
mat-menu-item
(click)="setActiveLang(lang.id)">
<span class="flex items-center">
<ng-container *ngTemplateOutlet="flagImage; context: {$implicit: lang.id}"></ng-container>
<span class="ml-3">{{lang.label}}</span>
</span>
</button>
</ng-container>
</mat-menu>
<!-- Flag image template -->
<ng-template
let-lang
#flagImage>
<span class="relative w-6 shadow rounded-sm overflow-hidden">
<span class="absolute inset-0 ring-1 ring-inset ring-black ring-opacity-10"></span>
<img
class="w-full"
[src]="'assets/images/flags/' + flagCodes[lang].toUpperCase() + '.svg'"
[alt]="'Flag image for ' + lang">
</span>
</ng-template>

View File

@@ -1,153 +0,0 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { take } from 'rxjs/operators';
import { AvailableLangs, TranslocoService } from '@ngneat/transloco';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation';
@Component({
selector : 'language',
templateUrl : './language.component.html',
encapsulation : ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
exportAs : 'language'
})
export class LanguageComponent implements OnInit, OnDestroy
{
availableLangs: AvailableLangs;
activeLang: string;
flagCodes: any;
/**
* Constructor
*/
constructor(
private _changeDetectorRef: ChangeDetectorRef,
private _fuseNavigationService: FuseNavigationService,
private _translocoService: TranslocoService
)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
// Get the available languages from transloco
this.availableLangs = this._translocoService.getAvailableLangs();
// Subscribe to language changes
this._translocoService.langChanges$.subscribe((activeLang) => {
// Get the active lang
this.activeLang = activeLang;
// Update the navigation
this._updateNavigation(activeLang);
});
// Set the country iso codes for languages for flags
this.flagCodes = {
'en': 'us',
'tr': 'tr'
};
}
/**
* On destroy
*/
ngOnDestroy(): void
{
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Set the active lang
*
* @param lang
*/
setActiveLang(lang: string): void
{
// Set the active lang
this._translocoService.setActiveLang(lang);
}
/**
* Track by function for ngFor loops
*
* @param index
* @param item
*/
trackByFn(index: number, item: any): any
{
return item.id || index;
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Update the navigation
*
* @param lang
* @private
*/
private _updateNavigation(lang: string): void
{
// For the demonstration purposes, we will only update the Dashboard names
// from the navigation but you can do a full swap and change the entire
// navigation data.
//
// You can import the data from a file or request it from your backend,
// it's up to you.
// Get the component -> navigation data -> item
const navComponent = this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>('mainNavigation');
// Return if the navigation component does not exist
if ( !navComponent )
{
return null;
}
// Get the flat navigation data
const navigation = navComponent.navigation;
// Get the Project dashboard item and update its title
const projectDashboardItem = this._fuseNavigationService.getItem('dashboards.project', navigation);
if ( projectDashboardItem )
{
this._translocoService.selectTranslate('Project').pipe(take(1))
.subscribe((translation) => {
// Set the title
projectDashboardItem.title = translation;
// Refresh the navigation component
navComponent.refresh();
});
}
// Get the Analytics dashboard item and update its title
const analyticsDashboardItem = this._fuseNavigationService.getItem('dashboards.analytics', navigation);
if ( analyticsDashboardItem )
{
this._translocoService.selectTranslate('Analytics').pipe(take(1))
.subscribe((translation) => {
// Set the title
analyticsDashboardItem.title = translation;
// Refresh the navigation component
navComponent.refresh();
});
}
}
}

View File

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

View File

@@ -46,7 +46,7 @@
<!-- Content -->
<div class="relative flex flex-col flex-auto sm:max-h-120 divide-y overflow-y-auto bg-card">
<!-- Messages -->
<ng-container *ngFor="let message of messages; trackBy: trackByFn">
<ng-container *ngFor="let message of messages">
<div
class="flex group hover:bg-gray-50 dark:hover:bg-black dark:hover:bg-opacity-5"
[ngClass]="{'unread': !message.read}">

View File

@@ -155,17 +155,6 @@ export class MessagesComponent implements OnInit, OnChanges, OnDestroy
this._messagesService.delete(message.id).subscribe();
}
/**
* Track by function for ngFor loops
*
* @param index
* @param item
*/
trackByFn(index: number, item: any): any
{
return item.id || index;
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------

View File

@@ -46,7 +46,7 @@
<!-- Content -->
<div class="relative flex flex-col flex-auto sm:max-h-120 divide-y overflow-y-auto bg-card">
<!-- Notifications -->
<ng-container *ngFor="let notification of notifications; trackBy: trackByFn">
<ng-container *ngFor="let notification of notifications">
<div
class="flex group hover:bg-gray-50 dark:hover:bg-black dark:hover:bg-opacity-5"
[ngClass]="{'unread': !notification.read}">

View File

@@ -155,17 +155,6 @@ export class NotificationsComponent implements OnChanges, OnInit, OnDestroy
this._notificationsService.delete(notification.id).subscribe();
}
/**
* Track by function for ngFor loops
*
* @param index
* @param item
*/
trackByFn(index: number, item: any): any
{
return item.id || index;
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------

View File

@@ -31,7 +31,7 @@
*ngIf="results && !results.length">
No results found!
</mat-option>
<ng-container *ngFor="let result of results; trackBy: trackByFn">
<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">
@@ -73,7 +73,7 @@
*ngIf="results && !results.length">
No results found!
</mat-option>
<ng-container *ngFor="let result of results; trackBy: trackByFn">
<ng-container *ngFor="let result of results">
<mat-option
class="group relative h-14 px-5 py-0 text-md"
[routerLink]="result.link">

View File

@@ -200,15 +200,4 @@ export class SearchComponent implements OnChanges, OnInit, OnDestroy
// Close the search
this.opened = false;
}
/**
* Track by function for ngFor loops
*
* @param index
* @param item
*/
trackByFn(index: number, item: any): any
{
return item.id || index;
}
}

View File

@@ -1,6 +1,6 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { BlockScrollStrategy, Overlay } from '@angular/cdk/overlay';
import { Overlay } from '@angular/cdk/overlay';
import { MAT_AUTOCOMPLETE_SCROLL_STRATEGY, MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
@@ -28,7 +28,7 @@ import { SearchComponent } from 'app/layout/common/search/search.component';
providers : [
{
provide : MAT_AUTOCOMPLETE_SCROLL_STRATEGY,
useFactory: (overlay: Overlay) => (): BlockScrollStrategy => overlay.scrollStrategies.block(),
useFactory: (overlay: Overlay) => () => overlay.scrollStrategies.block(),
deps : [Overlay]
}
]

View File

@@ -94,7 +94,7 @@
<!-- Shortcuts -->
<div class="grid grid-cols-2 grid-flow-row">
<!-- Shortcut -->
<ng-container *ngFor="let shortcut of shortcuts; trackBy: trackByFn">
<ng-container *ngFor="let shortcut of shortcuts">
<div class="relative group flex flex-col overflow-hidden bg-card border-r border-b even:border-r-0 hover:bg-gray-50 dark:hover:bg-black dark:hover:bg-opacity-5">
<ng-container *ngIf="mode === 'modify'">
<div

View File

@@ -176,7 +176,7 @@ export class ShortcutsComponent implements OnChanges, OnInit, OnDestroy
*/
save(): void
{
// Get the data from the form
// Get the mock-api from the form
const shortcut = this.shortcutForm.value;
// If there is an id, update it...
@@ -199,7 +199,7 @@ export class ShortcutsComponent implements OnChanges, OnInit, OnDestroy
*/
delete(): void
{
// Get the data from the form
// Get the mock-api from the form
const shortcut = this.shortcutForm.value;
// Delete
@@ -209,17 +209,6 @@ export class ShortcutsComponent implements OnChanges, OnInit, OnDestroy
this.mode = 'modify';
}
/**
* Track by function for ngFor loops
*
* @param index
* @param item
*/
trackByFn(index: number, item: any): any
{
return item.id || index;
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------

View File

@@ -217,7 +217,7 @@ export class LayoutComponent implements OnInit, OnDestroy
const paths = route.pathFromRoot;
paths.forEach((path) => {
// Check if there is a 'layout' data
// Check if there is a 'layout' mock-api
if ( path.routeConfig && path.routeConfig.data && path.routeConfig.data.layout )
{
// Set the layout

View File

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

View File

@@ -3,7 +3,7 @@ import { ActivatedRoute, Data, Router } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation';
import { FuseNavigationService } from '@fuse/components/navigation';
import { InitialData } from 'app/app.types';
@Component({
@@ -50,7 +50,7 @@ export class CenteredLayoutComponent implements OnInit, OnDestroy
*/
ngOnInit(): void
{
// Subscribe to the resolved route data
// Subscribe to the resolved route mock-api
this._activatedRoute.data.subscribe((data: Data) => {
this.data = data.initialData;
});
@@ -87,7 +87,7 @@ export class CenteredLayoutComponent implements OnInit, OnDestroy
toggleNavigation(name: string): void
{
// Get the navigation
const navigation = this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(name);
const navigation = this._fuseNavigationService.getComponent(name);
if ( navigation )
{

View File

@@ -7,7 +7,6 @@ import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { FuseFullscreenModule } from '@fuse/components/fullscreen';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { LanguageModule } from 'app/layout/common/language/language.module';
import { 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';
@@ -29,7 +28,6 @@ import { CenteredLayoutComponent } from 'app/layout/layouts/horizontal/centered/
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
LanguageModule,
MessagesModule,
NotificationsModule,
SearchModule,

View File

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

View File

@@ -3,7 +3,7 @@ import { ActivatedRoute, Data, Router } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation';
import { FuseNavigationService } from '@fuse/components/navigation';
import { InitialData } from 'app/app.types';
@Component({
@@ -50,7 +50,7 @@ export class EnterpriseLayoutComponent implements OnInit, OnDestroy
*/
ngOnInit(): void
{
// Subscribe to the resolved route data
// Subscribe to the resolved route mock-api
this._activatedRoute.data.subscribe((data: Data) => {
this.data = data.initialData;
});
@@ -87,7 +87,7 @@ export class EnterpriseLayoutComponent implements OnInit, OnDestroy
toggleNavigation(name: string): void
{
// Get the navigation
const navigation = this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(name);
const navigation = this._fuseNavigationService.getComponent(name);
if ( navigation )
{

View File

@@ -7,7 +7,6 @@ import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { FuseFullscreenModule } from '@fuse/components/fullscreen';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { LanguageModule } from 'app/layout/common/language/language.module';
import { 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';
@@ -29,7 +28,6 @@ import { EnterpriseLayoutComponent } from 'app/layout/layouts/horizontal/enterpr
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
LanguageModule,
MessagesModule,
NotificationsModule,
SearchModule,

View File

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

View File

@@ -3,7 +3,7 @@ import { ActivatedRoute, Data, Router } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation';
import { FuseNavigationService } from '@fuse/components/navigation';
import { InitialData } from 'app/app.types';
@Component({
@@ -50,7 +50,7 @@ export class MaterialLayoutComponent implements OnInit, OnDestroy
*/
ngOnInit(): void
{
// Subscribe to the resolved route data
// Subscribe to the resolved route mock-api
this._activatedRoute.data.subscribe((data: Data) => {
this.data = data.initialData;
});
@@ -87,7 +87,7 @@ export class MaterialLayoutComponent implements OnInit, OnDestroy
toggleNavigation(name: string): void
{
// Get the navigation
const navigation = this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(name);
const navigation = this._fuseNavigationService.getComponent(name);
if ( navigation )
{

View File

@@ -7,7 +7,6 @@ import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { FuseFullscreenModule } from '@fuse/components/fullscreen';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { LanguageModule } from 'app/layout/common/language/language.module';
import { 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';
@@ -29,7 +28,6 @@ import { MaterialLayoutComponent } from 'app/layout/layouts/horizontal/material/
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
LanguageModule,
MessagesModule,
NotificationsModule,
SearchModule,

View File

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

View File

@@ -3,7 +3,7 @@ import { ActivatedRoute, Data, Router } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation';
import { FuseNavigationService } from '@fuse/components/navigation';
import { InitialData } from 'app/app.types';
@Component({
@@ -50,7 +50,7 @@ export class ModernLayoutComponent implements OnInit, OnDestroy
*/
ngOnInit(): void
{
// Subscribe to the resolved route data
// Subscribe to the resolved route mock-api
this._activatedRoute.data.subscribe((data: Data) => {
this.data = data.initialData;
});
@@ -87,7 +87,7 @@ export class ModernLayoutComponent implements OnInit, OnDestroy
toggleNavigation(name: string): void
{
// Get the navigation
const navigation = this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(name);
const navigation = this._fuseNavigationService.getComponent(name);
if ( navigation )
{

View File

@@ -7,7 +7,6 @@ import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { FuseFullscreenModule } from '@fuse/components/fullscreen';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { LanguageModule } from 'app/layout/common/language/language.module';
import { 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';
@@ -29,7 +28,6 @@ import { ModernLayoutComponent } from 'app/layout/layouts/horizontal/modern/mode
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
LanguageModule,
MessagesModule,
NotificationsModule,
SearchModule,

View File

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

View File

@@ -3,7 +3,7 @@ import { ActivatedRoute, Data, Router } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation';
import { FuseNavigationService } from '@fuse/components/navigation';
import { InitialData } from 'app/app.types';
@Component({
@@ -50,7 +50,7 @@ export class ClassicLayoutComponent implements OnInit, OnDestroy
*/
ngOnInit(): void
{
// Subscribe to the resolved route data
// Subscribe to the resolved route mock-api
this._activatedRoute.data.subscribe((data: Data) => {
this.data = data.initialData;
});
@@ -87,7 +87,7 @@ export class ClassicLayoutComponent implements OnInit, OnDestroy
toggleNavigation(name: string): void
{
// Get the navigation
const navigation = this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(name);
const navigation = this._fuseNavigationService.getComponent(name);
if ( navigation )
{

View File

@@ -7,7 +7,6 @@ import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { FuseFullscreenModule } from '@fuse/components/fullscreen';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { LanguageModule } from 'app/layout/common/language/language.module';
import { 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';
@@ -29,7 +28,6 @@ import { ClassicLayoutComponent } from 'app/layout/layouts/vertical/classic/clas
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
LanguageModule,
MessagesModule,
NotificationsModule,
SearchModule,

View File

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

View File

@@ -3,7 +3,7 @@ import { ActivatedRoute, Data, Router } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation';
import { FuseNavigationService } from '@fuse/components/navigation';
import { InitialData } from 'app/app.types';
@Component({
@@ -50,7 +50,7 @@ export class ClassyLayoutComponent implements OnInit, OnDestroy
*/
ngOnInit(): void
{
// Subscribe to the resolved route data
// Subscribe to the resolved route mock-api
this._activatedRoute.data.subscribe((data: Data) => {
this.data = data.initialData;
});
@@ -87,7 +87,7 @@ export class ClassyLayoutComponent implements OnInit, OnDestroy
toggleNavigation(name: string): void
{
// Get the navigation
const navigation = this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(name);
const navigation = this._fuseNavigationService.getComponent(name);
if ( navigation )
{

View File

@@ -7,7 +7,6 @@ import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { FuseFullscreenModule } from '@fuse/components/fullscreen/fullscreen.module';
import { LanguageModule } from 'app/layout/common/language/language.module';
import { 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';
@@ -29,7 +28,6 @@ import { ClassyLayoutComponent } from 'app/layout/layouts/vertical/classy/classy
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
LanguageModule,
MessagesModule,
NotificationsModule,
SearchModule,

View File

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

View File

@@ -3,7 +3,7 @@ import { ActivatedRoute, Data, Router } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation';
import { FuseNavigationService } from '@fuse/components/navigation';
import { InitialData } from 'app/app.types';
@Component({
@@ -50,7 +50,7 @@ export class CompactLayoutComponent implements OnInit, OnDestroy
*/
ngOnInit(): void
{
// Subscribe to the resolved route data
// Subscribe to the resolved route mock-api
this._activatedRoute.data.subscribe((data: Data) => {
this.data = data.initialData;
});
@@ -87,7 +87,7 @@ export class CompactLayoutComponent implements OnInit, OnDestroy
toggleNavigation(name: string): void
{
// Get the navigation
const navigation = this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(name);
const navigation = this._fuseNavigationService.getComponent(name);
if ( navigation )
{

View File

@@ -7,7 +7,6 @@ import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { FuseFullscreenModule } from '@fuse/components/fullscreen';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { LanguageModule } from 'app/layout/common/language/language.module';
import { 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';
@@ -29,7 +28,6 @@ import { CompactLayoutComponent } from 'app/layout/layouts/vertical/compact/comp
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
LanguageModule,
MessagesModule,
NotificationsModule,
SearchModule,

View File

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

View File

@@ -3,7 +3,7 @@ import { ActivatedRoute, Data, Router } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation';
import { FuseNavigationService } from '@fuse/components/navigation';
import { InitialData } from 'app/app.types';
@Component({
@@ -51,7 +51,7 @@ export class DenseLayoutComponent implements OnInit, OnDestroy
*/
ngOnInit(): void
{
// Subscribe to the resolved route data
// Subscribe to the resolved route mock-api
this._activatedRoute.data.subscribe((data: Data) => {
this.data = data.initialData;
});
@@ -88,7 +88,7 @@ export class DenseLayoutComponent implements OnInit, OnDestroy
toggleNavigation(name: string): void
{
// Get the navigation
const navigation = this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(name);
const navigation = this._fuseNavigationService.getComponent(name);
if ( navigation )
{

View File

@@ -7,7 +7,6 @@ import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { FuseFullscreenModule } from '@fuse/components/fullscreen';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { LanguageModule } from 'app/layout/common/language/language.module';
import { 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';
@@ -29,7 +28,6 @@ import { DenseLayoutComponent } from 'app/layout/layouts/vertical/dense/dense.co
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
LanguageModule,
MessagesModule,
NotificationsModule,
SearchModule,

View File

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

View File

@@ -3,7 +3,7 @@ import { ActivatedRoute, Data, Router } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation';
import { FuseNavigationService } from '@fuse/components/navigation';
import { InitialData } from 'app/app.types';
@Component({
@@ -50,7 +50,7 @@ export class FuturisticLayoutComponent implements OnInit, OnDestroy
*/
ngOnInit(): void
{
// Subscribe to the resolved route data
// Subscribe to the resolved route mock-api
this._activatedRoute.data.subscribe((data: Data) => {
this.data = data.initialData;
});
@@ -87,7 +87,7 @@ export class FuturisticLayoutComponent implements OnInit, OnDestroy
toggleNavigation(name: string): void
{
// Get the navigation
const navigation = this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(name);
const navigation = this._fuseNavigationService.getComponent(name);
if ( navigation )
{

View File

@@ -7,7 +7,6 @@ import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { FuseFullscreenModule } from '@fuse/components/fullscreen';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { LanguageModule } from 'app/layout/common/language/language.module';
import { 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';
@@ -29,7 +28,6 @@ import { FuturisticLayoutComponent } from 'app/layout/layouts/vertical/futuristi
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
LanguageModule,
MessagesModule,
NotificationsModule,
SearchModule,

View File

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

View File

@@ -3,7 +3,7 @@ import { ActivatedRoute, Data, Router } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation';
import { FuseNavigationService } from '@fuse/components/navigation';
import { InitialData } from 'app/app.types';
@Component({
@@ -50,7 +50,7 @@ export class ThinLayoutComponent implements OnInit, OnDestroy
*/
ngOnInit(): void
{
// Subscribe to the resolved route data
// Subscribe to the resolved route mock-api
this._activatedRoute.data.subscribe((data: Data) => {
this.data = data.initialData;
});
@@ -87,7 +87,7 @@ export class ThinLayoutComponent implements OnInit, OnDestroy
toggleNavigation(name: string): void
{
// Get the navigation
const navigation = this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(name);
const navigation = this._fuseNavigationService.getComponent(name);
if ( navigation )
{

View File

@@ -7,7 +7,6 @@ import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { FuseFullscreenModule } from '@fuse/components/fullscreen';
import { FuseNavigationModule } from '@fuse/components/navigation';
import { LanguageModule } from 'app/layout/common/language/language.module';
import { 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';
@@ -29,7 +28,6 @@ import { ThinLayoutComponent } from 'app/layout/layouts/vertical/thin/thin.compo
MatMenuModule,
FuseFullscreenModule,
FuseNavigationModule,
LanguageModule,
MessagesModule,
NotificationsModule,
SearchModule,

View File

@@ -295,12 +295,12 @@ export class ContactsMockApi
const reader = new FileReader();
// Resolve the promise on success
reader.onload = (): void => {
reader.onload = () => {
resolve(reader.result);
};
// Reject the promise on error
reader.onerror = (e): void => {
reader.onerror = (e) => {
reject(e);
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,4 @@
import { AcademyMockApi } from 'app/mock-api/apps/academy/api';
import { ActivitiesMockApi } from 'app/mock-api/pages/activities/api';
import { AnalyticsMockApi } from 'app/mock-api/dashboards/analytics/api';
import { AuthMockApi } from 'app/mock-api/common/auth/api';
import { CalendarMockApi } from 'app/mock-api/apps/calendar/api';
@@ -22,7 +21,6 @@ import { UserMockApi } from 'app/mock-api/common/user/api';
export const mockApiServices = [
AcademyMockApi,
ActivitiesMockApi,
AnalyticsMockApi,
AuthMockApi,
CalendarMockApi,

View File

@@ -1,38 +0,0 @@
import { Injectable } from '@angular/core';
import { cloneDeep } from 'lodash-es';
import { FuseMockApiService } from '@fuse/lib/mock-api';
import { activities as activitiesData } from 'app/mock-api/pages/activities/data';
@Injectable({
providedIn: 'root'
})
export class ActivitiesMockApi
{
private _activities: any = activitiesData;
/**
* Constructor
*/
constructor(private _fuseMockApiService: FuseMockApiService)
{
// Register Mock API handlers
this.registerHandlers();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Register Mock API handlers
*/
registerHandlers(): void
{
// -----------------------------------------------------------------------------------------------------
// @ Activities - GET
// -----------------------------------------------------------------------------------------------------
this._fuseMockApiService
.onGet('api/pages/activities')
.reply(() => [200, cloneDeep(this._activities)]);
}
}

View File

@@ -1,89 +0,0 @@
/* eslint-disable */
import * as moment from 'moment';
import { Activity } from 'app/modules/admin/pages/activities/activities.types';
export const activities: Activity[] = [
{
id : '493190c9-5b61-4912-afe5-78c21f1044d7',
icon : 'heroicons_solid:star',
description : 'Your submission has been accepted',
date : moment().subtract(25, 'minutes').toISOString(), // 25 minutes ago
extraContent: `<div class="font-bold">Congratulations for your acceptance!</div><br>
<div>Hi Brian,<br>Your submission has been accepted and you are ready to move into the next phase. Once you are ready, reach out to me and we will ...</div>`
},
{
id : '6e3e97e5-effc-4fb7-b730-52a151f0b641',
image : 'assets/images/avatars/male-04.jpg',
description : '<strong>Leo Gill</strong> added you to <strong>Top Secret Project</strong> group and assigned you as a <strong>Project Manager</strong>',
date : moment().subtract(50, 'minutes').toISOString(), // 50 minutes ago
linkedContent: 'Top Secret Project',
link : '/dashboards/project',
useRouter : true
},
{
id : 'b91ccb58-b06c-413b-b389-87010e03a120',
icon : 'heroicons_solid:mail',
description : 'You have 15 unread mails across 3 mailboxes',
date : moment().subtract(3, 'hours').toISOString(), // 3 hours ago
linkedContent: 'Mailbox',
link : '/apps/mailbox',
useRouter : true
},
{
id : '541416c9-84a7-408a-8d74-27a43c38d797',
icon : 'heroicons_solid:refresh',
description : 'Your <strong>Docker container</strong> is ready to publish',
date : moment().subtract(5, 'hours').toISOString(), // 5 hours ago
linkedContent: 'Download the container',
link : '.',
useRouter : true
},
{
id : 'ef7b95a7-8e8b-4616-9619-130d9533add9',
image : 'assets/images/avatars/male-06.jpg',
description : '<strong>Roger Murray</strong> accepted your friend request',
date : moment().subtract(7, 'hours').toISOString(), // 7 hours ago
extraContent: `You have <span class="font-semibold">8</span> mutual friends.`
},
{
id : 'eb8aa470-635e-461d-88e1-23d9ea2a5665',
image : 'assets/images/avatars/female-04.jpg',
description: '<strong>Sophie Stone</strong> sent you a direct message',
date : moment().subtract(9, 'hours').toISOString() // 9 hours ago
},
{
id : 'b85c2338-cc98-4140-bbf8-c226ce4e395e',
icon : 'heroicons_solid:mail',
description : 'You have 3 new mails',
date : moment().subtract(1, 'day').toISOString(), // 1 day ago
extraContent : `<ol class="list-decimal list-inside space-y-2">
<li class="font-medium">Please review and sign the attached agreement</li>
<li class="font-medium">Delivery address confirmation</li>
<li class="font-medium">Previous clients and their invoices</li>
</ol>`,
linkedContent: 'Mailbox',
link : '/apps/mailbox',
useRouter : true
},
{
id : 'fd0f01b4-f3de-4333-add5-cd86850279f8',
image : 'assets/images/avatars/female-02.jpg',
description : '<strong>Tina Harris</strong> started a chat with you',
date : moment().subtract(1, 'day').toISOString(), // 1 day ago,
linkedContent: 'Go to Chat (Tina Harris)',
link : '/apps/chat/5636c0ba-fa47-42ca-9160-27340583041e',
useRouter : true
},
{
id : '8f8e1bf9-4661-4939-9e43-390957b60f42',
icon : 'heroicons_solid:star',
description: 'Your submission has been accepted and you are ready to sign-up for the final assigment which will be ready in 2 days',
date : moment().subtract(3, 'days').toISOString() // 3 days ago
},
{
id : '30af917b-7a6a-45d1-822f-9e7ad7f8bf69',
icon : 'heroicons_solid:refresh',
description: 'Your Vagrant container is ready to download',
date : moment().subtract(4, 'day').toISOString() // 4 days ago
}
];

View File

@@ -1 +0,0 @@
<router-outlet></router-outlet>

View File

@@ -1,17 +0,0 @@
import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core';
@Component({
selector : 'academy',
templateUrl : './academy.component.html',
encapsulation : ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AcademyComponent
{
/**
* Constructor
*/
constructor()
{
}
}

View File

@@ -1,44 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatSelectModule } from '@angular/material/select';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatTooltipModule } from '@angular/material/tooltip';
import { FuseFindByKeyPipeModule } from '@fuse/pipes/find-by-key';
import { SharedModule } from 'app/shared/shared.module';
import { academyRoutes } from 'app/modules/admin/apps/academy/academy.routing';
import { AcademyComponent } from 'app/modules/admin/apps/academy/academy.component';
import { AcademyDetailsComponent } from 'app/modules/admin/apps/academy/details/details.component';
import { AcademyListComponent } from 'app/modules/admin/apps/academy/list/list.component';
import { MatTabsModule } from '@angular/material/tabs';
@NgModule({
declarations: [
AcademyComponent,
AcademyDetailsComponent,
AcademyListComponent
],
imports : [
RouterModule.forChild(academyRoutes),
MatButtonModule,
MatFormFieldModule,
MatIconModule,
MatInputModule,
MatProgressBarModule,
MatSelectModule,
MatSidenavModule,
MatSlideToggleModule,
MatTooltipModule,
FuseFindByKeyPipeModule,
SharedModule,
MatTabsModule
]
})
export class AcademyModule
{
}

View File

@@ -1,110 +0,0 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { Category, Course } from 'app/modules/admin/apps/academy/academy.types';
import { AcademyService } from 'app/modules/admin/apps/academy/academy.service';
@Injectable({
providedIn: 'root'
})
export class AcademyCategoriesResolver implements Resolve<any>
{
/**
* Constructor
*/
constructor(private _academyService: AcademyService)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Resolver
*
* @param route
* @param state
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Category[]>
{
return this._academyService.getCategories();
}
}
@Injectable({
providedIn: 'root'
})
export class AcademyCoursesResolver implements Resolve<any>
{
/**
* Constructor
*/
constructor(private _academyService: AcademyService)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Resolver
*
* @param route
* @param state
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Course[]>
{
return this._academyService.getCourses();
}
}
@Injectable({
providedIn: 'root'
})
export class AcademyCourseResolver implements Resolve<any>
{
/**
* Constructor
*/
constructor(
private _router: Router,
private _academyService: AcademyService
)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Resolver
*
* @param route
* @param state
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Course>
{
return this._academyService.getCourseById(route.paramMap.get('id'))
.pipe(
// Error here means the requested task is not available
catchError((error) => {
// Log the error
console.error(error);
// Get the parent url
const parentUrl = state.url.split('/').slice(0, -1).join('/');
// Navigate to there
this._router.navigateByUrl(parentUrl);
// Throw an error
return throwError(error);
})
);
}
}

View File

@@ -1,32 +0,0 @@
import { Route } from '@angular/router';
import { AcademyComponent } from 'app/modules/admin/apps/academy/academy.component';
import { AcademyListComponent } from 'app/modules/admin/apps/academy/list/list.component';
import { AcademyDetailsComponent } from 'app/modules/admin/apps/academy/details/details.component';
import { AcademyCategoriesResolver, AcademyCourseResolver, AcademyCoursesResolver } from 'app/modules/admin/apps/academy/academy.resolvers';
export const academyRoutes: Route[] = [
{
path : '',
component: AcademyComponent,
resolve : {
categories: AcademyCategoriesResolver
},
children : [
{
path : '',
pathMatch: 'full',
component: AcademyListComponent,
resolve : {
courses: AcademyCoursesResolver
}
},
{
path : ':id',
component: AcademyDetailsComponent,
resolve : {
course: AcademyCourseResolver
}
}
]
}
];

View File

@@ -1,105 +0,0 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject, Observable, of, throwError } from 'rxjs';
import { map, switchMap, tap } from 'rxjs/operators';
import { Category, Course } from 'app/modules/admin/apps/academy/academy.types';
@Injectable({
providedIn: 'root'
})
export class AcademyService
{
// Private
private _categories: BehaviorSubject<Category[] | null> = new BehaviorSubject(null);
private _course: BehaviorSubject<Course | null> = new BehaviorSubject(null);
private _courses: BehaviorSubject<Course[] | null> = new BehaviorSubject(null);
/**
* Constructor
*/
constructor(private _httpClient: HttpClient)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Accessors
// -----------------------------------------------------------------------------------------------------
/**
* Getter for categories
*/
get categories$(): Observable<Category[]>
{
return this._categories.asObservable();
}
/**
* Getter for courses
*/
get courses$(): Observable<Course[]>
{
return this._courses.asObservable();
}
/**
* Getter for course
*/
get course$(): Observable<Course>
{
return this._course.asObservable();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Get categories
*/
getCategories(): Observable<Category[]>
{
return this._httpClient.get<Category[]>('api/apps/academy/categories').pipe(
tap((response: any) => {
this._categories.next(response);
})
);
}
/**
* Get courses
*/
getCourses(): Observable<Course[]>
{
return this._httpClient.get<Course[]>('api/apps/academy/courses').pipe(
tap((response: any) => {
this._courses.next(response);
})
);
}
/**
* Get course by id
*/
getCourseById(id: string): Observable<Course>
{
return this._httpClient.get<Course>('api/apps/academy/courses/course', {params: {id}}).pipe(
map((course) => {
// Update the course
this._course.next(course);
// Return the course
return course;
}),
switchMap((course) => {
if ( !course )
{
return throwError('Could not found course with id of ' + id + '!');
}
return of(course);
})
);
}
}

View File

@@ -1,29 +0,0 @@
export interface Category
{
id?: string;
title?: string;
slug?: string;
}
export interface Course
{
id?: string;
title?: string;
slug?: string;
description?: string;
category?: string;
duration?: number;
steps?: {
order?: number;
title?: string;
subtitle?: string;
content?: string;
}[];
totalSteps?: number;
updatedAt?: number;
featured?: boolean;
progress?: {
currentStep?: number;
completed?: number;
};
}

View File

@@ -1,201 +0,0 @@
<div class="absolute inset-0 flex flex-col min-w-0 overflow-hidden">
<mat-drawer-container class="flex-auto h-full">
<!-- Drawer -->
<mat-drawer
class="w-90 dark:bg-gray-900"
[autoFocus]="false"
[mode]="drawerMode"
[opened]="drawerOpened"
#matDrawer>
<div class="flex flex-col items-start p-8 border-b">
<!-- Back to courses -->
<a
class="inline-flex items-center leading-6 text-primary hover:underline"
[routerLink]="['..']">
<span class="inline-flex items-center">
<mat-icon
class="icon-size-5 text-current"
[svgIcon]="'heroicons_solid:arrow-sm-left'"></mat-icon>
<span class="ml-1.5 font-medium leading-5">Back to courses</span>
</span>
</a>
<!-- Course category -->
<ng-container *ngIf="(course.category | fuseFindByKey:'slug':categories) as category">
<div
class="mt-7 py-0.5 px-3 rounded-full text-sm font-semibold"
[ngClass]="{'text-blue-800 bg-blue-100 dark:text-blue-50 dark:bg-blue-500': category.slug === 'web',
'text-green-800 bg-green-100 dark:text-green-50 dark:bg-green-500': category.slug === 'android',
'text-pink-800 bg-pink-100 dark:text-pink-50 dark:bg-pink-500': category.slug === 'cloud',
'text-amber-800 bg-amber-100 dark:text-amber-50 dark:bg-amber-500': category.slug === 'firebase'}">
{{category.title}}
</div>
</ng-container>
<!-- Course title & description -->
<div class="mt-3 text-2xl font-semibold">{{course.title}}</div>
<div class="text-secondary">{{course.description}}</div>
<!-- Course time -->
<div class="mt-6 flex items-center leading-5 text-md text-secondary">
<mat-icon
class="icon-size-5 text-hint"
[svgIcon]="'heroicons_solid:clock'"></mat-icon>
<div class="ml-1.5">{{course.duration}} minutes</div>
</div>
</div>
<!-- Steps -->
<div class="py-2 px-8">
<ol>
<ng-container *ngFor="let step of course.steps; let last = last; trackBy: trackByFn">
<li
class="relative group py-6"
[class.current-step]="step.order === currentStep">
<ng-container *ngIf="!last">
<div
class="absolute top-6 left-4 w-0.5 h-full -ml-px"
[ngClass]="{'bg-primary': step.order < currentStep,
'bg-gray-300 dark:bg-gray-600': step.order >= currentStep}"></div>
</ng-container>
<div
class="relative flex items-start cursor-pointer"
(click)="goToStep(step.order)">
<div
class="flex flex-0 items-center justify-center w-8 h-8 rounded-full ring-2 ring-inset ring-transparent bg-card dark:bg-default"
[ngClass]="{'bg-primary dark:bg-primary text-on-primary group-hover:bg-primary-800': step.order < currentStep,
'ring-primary': step.order === currentStep,
'ring-gray-300 dark:ring-gray-600 group-hover:ring-gray-400': step.order > currentStep}">
<!-- Check icon, show if the step is completed -->
<ng-container *ngIf="step.order < currentStep">
<mat-icon
class="icon-size-5 text-current"
[svgIcon]="'heroicons_solid:check'"></mat-icon>
</ng-container>
<!-- Step order, show if the step is the current step -->
<ng-container *ngIf="step.order === currentStep">
<div class="text-md font-semibold text-primary dark:text-primary-500">{{step.order + 1}}</div>
</ng-container>
<!-- Step order, show if the step is not completed -->
<ng-container *ngIf="step.order > currentStep">
<div class="text-md font-semibold text-hint group-hover:text-secondary">{{step.order + 1}}</div>
</ng-container>
</div>
<div class="ml-4">
<div class="font-medium leading-4">{{step.title}}</div>
<div class="mt-1.5 text-md leading-4 text-secondary">{{step.subtitle}}</div>
</div>
</div>
</li>
</ng-container>
</ol>
</div>
</mat-drawer>
<!-- Drawer content -->
<mat-drawer-content class="flex flex-col overflow-hidden">
<!-- Header -->
<div class="lg:hidden flex flex-0 items-center py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8 border-b lg:border-b-0 bg-card dark:bg-transparent">
<!-- Title & Actions -->
<button
mat-icon-button
[routerLink]="['..']">
<mat-icon [svgIcon]="'heroicons_outline:arrow-sm-left'"></mat-icon>
</button>
<h2 class="ml-2.5 text-md sm:text-xl font-medium tracking-tight truncate">
{{course.title}}
</h2>
</div>
<mat-progress-bar
class="hidden lg:block flex-0 h-0.5 w-full"
[value]="100 * (currentStep + 1) / course.totalSteps"></mat-progress-bar>
<!-- Main -->
<div
class="flex-auto overflow-y-auto"
cdkScrollable>
<!-- Steps -->
<mat-tab-group
class="fuse-mat-no-header"
[animationDuration]="'200'"
#courseSteps>
<ng-container *ngFor="let step of course.steps; trackBy: trackByFn">
<mat-tab>
<ng-template matTabContent>
<div
class="prose prose-sm max-w-3xl mx-auto sm:my-2 lg:mt-4 p-6 sm:p-10 sm:py-12 rounded-2xl shadow overflow-hidden bg-card"
[innerHTML]="step.content"></div>
</ng-template>
</mat-tab>
</ng-container>
</mat-tab-group>
<!-- Navigation - Desktop -->
<div class="z-10 sticky hidden lg:flex bottom-4 p-4">
<div class="flex items-center justify-center mx-auto p-2 rounded-full shadow-lg bg-primary">
<button
class="flex-0"
mat-flat-button
[color]="'primary'"
(click)="goToPreviousStep()">
<mat-icon
class="mr-2"
[svgIcon]="'heroicons_outline:arrow-narrow-left'"></mat-icon>
<span class="mr-1">Prev</span>
</button>
<div class="flex items-center justify-center mx-2.5 font-medium leading-5 text-on-primary">
<span>{{currentStep + 1}}</span>
<span class="mx-0.5 text-hint">/</span>
<span>{{course.totalSteps}}</span>
</div>
<button
class="flex-0"
mat-flat-button
[color]="'primary'"
(click)="goToNextStep()">
<span class="ml-1">Next</span>
<mat-icon
class="ml-2"
[svgIcon]="'heroicons_outline:arrow-narrow-right'"></mat-icon>
</button>
</div>
</div>
</div>
<!-- Progress & Navigation - Mobile -->
<div class="lg:hidden flex items-center p-4 border-t bg-card">
<button
mat-icon-button
(click)="matDrawer.toggle()">
<mat-icon [svgIcon]="'heroicons_outline:view-list'"></mat-icon>
</button>
<div class="flex items-center justify-center ml-1 lg:ml-2 font-medium leading-5">
<span>{{currentStep + 1}}</span>
<span class="mx-0.5 text-hint">/</span>
<span>{{course.totalSteps}}</span>
</div>
<mat-progress-bar
class="flex-auto ml-6 rounded-full"
[value]="100 * (currentStep + 1) / course.totalSteps"></mat-progress-bar>
<button
class="ml-4"
mat-icon-button
(click)="goToPreviousStep()">
<mat-icon [svgIcon]="'heroicons_outline:arrow-narrow-left'"></mat-icon>
</button>
<button
class="ml-0.5"
mat-icon-button
(click)="goToNextStep()">
<mat-icon [svgIcon]="'heroicons_outline:arrow-narrow-right'"></mat-icon>
</button>
</div>
</mat-drawer-content>
</mat-drawer-container>
</div>

View File

@@ -1,204 +0,0 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { MatTabGroup } from '@angular/material/tabs';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { Category, Course } from 'app/modules/admin/apps/academy/academy.types';
import { AcademyService } from 'app/modules/admin/apps/academy/academy.service';
@Component({
selector : 'academy-details',
templateUrl : './details.component.html',
encapsulation : ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AcademyDetailsComponent implements OnInit, OnDestroy
{
@ViewChild('courseSteps', {static: true}) courseSteps: MatTabGroup;
categories: Category[];
course: Course;
currentStep: number = 0;
drawerMode: 'over' | 'side' = 'side';
drawerOpened: boolean = true;
private _unsubscribeAll: Subject<any> = new Subject<any>();
/**
* Constructor
*/
constructor(
@Inject(DOCUMENT) private _document: Document,
private _academyService: AcademyService,
private _changeDetectorRef: ChangeDetectorRef,
private _elementRef: ElementRef,
private _fuseMediaWatcherService: FuseMediaWatcherService
)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
// Get the categories
this._academyService.categories$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((categories: Category[]) => {
// Get the categories
this.categories = categories;
// Mark for check
this._changeDetectorRef.markForCheck();
});
// Get the course
this._academyService.course$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((course: Course) => {
// Get the course
this.course = course;
// Go to step
this.goToStep(course.progress.currentStep);
// Mark for check
this._changeDetectorRef.markForCheck();
});
// Subscribe to media changes
this._fuseMediaWatcherService.onMediaChange$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe(({matchingAliases}) => {
// Set the drawerMode and drawerOpened
if ( matchingAliases.includes('lg') )
{
this.drawerMode = 'side';
this.drawerOpened = true;
}
else
{
this.drawerMode = 'over';
this.drawerOpened = false;
}
// Mark for check
this._changeDetectorRef.markForCheck();
});
}
/**
* On destroy
*/
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Go to given step
*
* @param step
*/
goToStep(step: number): void
{
// Set the current step
this.currentStep = step;
// Go to the step
this.courseSteps.selectedIndex = this.currentStep;
// Mark for check
this._changeDetectorRef.markForCheck();
}
/**
* Go to previous step
*/
goToPreviousStep(): void
{
// Return if we already on the first step
if ( this.currentStep === 0 )
{
return;
}
// Go to step
this.goToStep(this.currentStep - 1);
// Scroll the current step selector from sidenav into view
this._scrollCurrentStepElementIntoView();
}
/**
* Go to next step
*/
goToNextStep(): void
{
// Return if we already on the last step
if ( this.currentStep === this.course.totalSteps - 1 )
{
return;
}
// Go to step
this.goToStep(this.currentStep + 1);
// Scroll the current step selector from sidenav into view
this._scrollCurrentStepElementIntoView();
}
/**
* Track by function for ngFor loops
*
* @param index
* @param item
*/
trackByFn(index: number, item: any): any
{
return item.id || index;
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Scrolls the current step element from
* sidenav into the view. This only happens when
* previous/next buttons pressed as we don't want
* to change the scroll position of the sidebar
* when the user actually clicks around the sidebar.
*
* @private
*/
private _scrollCurrentStepElementIntoView(): void
{
// Wrap everything into setTimeout so we can make sure that the 'current-step' class points to correct element
setTimeout(() => {
// Get the current step element and scroll it into view
const currentStepElement = this._document.getElementsByClassName('current-step')[0];
if ( currentStepElement )
{
currentStepElement.scrollIntoView({
behavior: 'smooth',
block : 'start'
});
}
});
}
}

View File

@@ -1,196 +0,0 @@
<div
class="absolute inset-0 flex flex-col min-w-0 overflow-y-auto"
cdkScrollable>
<!-- Header -->
<div class="relative flex-0 py-8 px-4 sm:p-16 overflow-hidden bg-gray-800 dark">
<!-- Background - @formatter:off -->
<!-- Rings -->
<svg class="absolute inset-0 pointer-events-none"
viewBox="0 0 960 540" width="100%" height="100%" preserveAspectRatio="xMidYMax slice" xmlns="http://www.w3.org/2000/svg">
<g class="text-gray-700 opacity-25" fill="none" stroke="currentColor" stroke-width="100">
<circle r="234" cx="196" cy="23"></circle>
<circle r="234" cx="790" cy="491"></circle>
</g>
</svg>
<!-- @formatter:on -->
<div class="z-10 relative flex flex-col items-center">
<h2 class="text-xl font-semibold">FUSE ACADEMY</h2>
<div class="mt-1 text-4xl sm:text-7xl font-extrabold tracking-tight leading-tight text-center">
What do you want to learn today?
</div>
<div class="max-w-2xl mt-6 sm:text-2xl text-center tracking-tight text-secondary">
Our courses will step you through the process of a building small applications, or adding new features to existing applications.
</div>
</div>
</div>
<!-- Main -->
<div class="flex flex-auto p-6 sm:p-10">
<div class="flex flex-col flex-auto w-full max-w-xs sm:max-w-5xl mx-auto">
<!-- Filters -->
<div class="flex flex-col sm:flex-row items-center justify-between w-full max-w-xs sm:max-w-none">
<mat-form-field class="fuse-mat-no-subscript w-full sm:w-36">
<mat-select
[value]="'all'"
(selectionChange)="filterByCategory($event)">
<mat-option [value]="'all'">All</mat-option>
<ng-container *ngFor="let category of categories; trackBy: trackByFn">
<mat-option [value]="category.slug">{{category.title}}</mat-option>
</ng-container>
</mat-select>
</mat-form-field>
<mat-form-field
class="fuse-mat-no-subscript w-full sm:w-72 mt-4 sm:mt-0 sm:ml-4"
[floatLabel]="'always'">
<mat-icon
matPrefix
class="icon-size-5"
[svgIcon]="'heroicons_solid:search'"></mat-icon>
<input
(input)="filterByQuery(query.value)"
placeholder="Search by title or description"
matInput
#query>
</mat-form-field>
<mat-slide-toggle
class="mt-8 sm:mt-0 sm:ml-auto"
[color]="'primary'"
(change)="toggleCompleted($event)">
Hide completed
</mat-slide-toggle>
</div>
<!-- Courses -->
<ng-container *ngIf="this.filteredCourses.length; else noCourses">
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8 mt-8 sm:mt-10">
<ng-container *ngFor="let course of filteredCourses; trackBy: trackByFn">
<!-- Course -->
<div class="flex flex-col h-96 shadow rounded-2xl overflow-hidden bg-card">
<div class="flex flex-col p-6">
<div class="flex items-center justify-between">
<!-- Course category -->
<ng-container *ngIf="(course.category | fuseFindByKey:'slug':categories) as category">
<div
class="py-0.5 px-3 rounded-full text-sm font-semibold"
[ngClass]="{'text-blue-800 bg-blue-100 dark:text-blue-50 dark:bg-blue-500': category.slug === 'web',
'text-green-800 bg-green-100 dark:text-green-50 dark:bg-green-500': category.slug === 'android',
'text-pink-800 bg-pink-100 dark:text-pink-50 dark:bg-pink-500': category.slug === 'cloud',
'text-amber-800 bg-amber-100 dark:text-amber-50 dark:bg-amber-500': category.slug === 'firebase'}">
{{category.title}}
</div>
</ng-container>
<!-- Completed at least once -->
<div class="flex items-center">
<ng-container *ngIf="course.progress.completed > 0">
<mat-icon
class="icon-size-5 text-green-600"
[svgIcon]="'heroicons_solid:badge-check'"
[matTooltip]="'You completed this course at least once'"></mat-icon>
</ng-container>
</div>
</div>
<!-- Course title & description -->
<div class="mt-4 text-lg font-medium">{{course.title}}</div>
<div class="mt-0.5 line-clamp-2 text-secondary">{{course.description}}</div>
<div class="w-12 h-1 my-6 border-t-2"></div>
<!-- Course time -->
<div class="flex items-center leading-5 text-md text-secondary">
<mat-icon
class="icon-size-5 text-hint"
[svgIcon]="'heroicons_solid:clock'"></mat-icon>
<div class="ml-1.5">{{course.duration}} minutes</div>
</div>
<!-- Course completion -->
<div class="flex items-center mt-2 leading-5 text-md text-secondary">
<mat-icon
class="icon-size-5 text-hint"
[svgIcon]="'heroicons_solid:academic-cap'"></mat-icon>
<ng-container *ngIf="course.progress.completed === 0">
<div class="ml-1.5">Never completed</div>
</ng-container>
<ng-container *ngIf="course.progress.completed > 0">
<div class="ml-1.5">
<span>Completed</span>
<span class="ml-1">
<!-- Once -->
<ng-container *ngIf="course.progress.completed === 1">once</ng-container>
<!-- Twice -->
<ng-container *ngIf="course.progress.completed === 2">twice</ng-container>
<!-- Others -->
<ng-container *ngIf="course.progress.completed > 2">{{course.progress.completed}}
{{course.progress.completed | i18nPlural: {
'=0' : 'time',
'=1' : 'time',
'other': 'times'
} }}
</ng-container>
</span>
</div>
</ng-container>
</div>
</div>
<!-- Footer -->
<div class="flex flex-col w-full mt-auto">
<!-- Course progress -->
<div class="relative h-0.5">
<div
class="z-10 absolute inset-x-0 h-6 -mt-3"
[matTooltip]="course.progress.currentStep / course.totalSteps | percent"
[matTooltipPosition]="'above'"
[matTooltipClass]="'-mb-0.5'"></div>
<mat-progress-bar
class="h-0.5"
[value]="(100 * course.progress.currentStep) / course.totalSteps"></mat-progress-bar>
</div>
<!-- Course launch button -->
<div class="px-6 py-4 text-right bg-gray-50 dark:bg-transparent">
<button
mat-stroked-button
[routerLink]="[course.id]">
<span class="inline-flex items-center">
<!-- Not started -->
<ng-container *ngIf="course.progress.currentStep === 0">
<!-- Never completed -->
<ng-container *ngIf="course.progress.completed === 0">
<span>Start</span>
</ng-container>
<!-- Completed before -->
<ng-container *ngIf="course.progress.completed > 0">
<span>Start again</span>
</ng-container>
</ng-container>
<!-- Started -->
<ng-container *ngIf="course.progress.currentStep > 0">
<span>Continue</span>
</ng-container>
<mat-icon
class="ml-1.5 icon-size-5"
[svgIcon]="'heroicons_solid:arrow-sm-right'"></mat-icon>
</span>
</button>
</div>
</div>
</div>
</ng-container>
</div>
</ng-container>
<!-- No courses -->
<ng-template #noCourses>
<div class="flex flex-auto flex-col items-center justify-center bg-gray-100 dark:bg-transparent">
<mat-icon
class="icon-size-20"
[svgIcon]="'iconsmind:file_search'"></mat-icon>
<div class="mt-6 text-2xl font-semibold tracking-tight text-secondary">No courses found!</div>
</div>
</ng-template>
</div>
</div>
</div>

View File

@@ -1,157 +0,0 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { MatSelectChange } from '@angular/material/select';
import { MatSlideToggleChange } from '@angular/material/slide-toggle';
import { BehaviorSubject, combineLatest, Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { AcademyService } from 'app/modules/admin/apps/academy/academy.service';
import { Category, Course } from 'app/modules/admin/apps/academy/academy.types';
@Component({
selector : 'academy-list',
templateUrl : './list.component.html',
encapsulation : ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AcademyListComponent implements OnInit, OnDestroy
{
categories: Category[];
courses: Course[];
filteredCourses: Course[];
filters: {
categorySlug$: BehaviorSubject<string>;
query$: BehaviorSubject<string>;
hideCompleted$: BehaviorSubject<boolean>;
} = {
categorySlug$ : new BehaviorSubject('all'),
query$ : new BehaviorSubject(''),
hideCompleted$: new BehaviorSubject(false)
};
private _unsubscribeAll: Subject<any> = new Subject<any>();
/**
* Constructor
*/
constructor(
private _activatedRoute: ActivatedRoute,
private _changeDetectorRef: ChangeDetectorRef,
private _router: Router,
private _academyService: AcademyService
)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
// Get the categories
this._academyService.categories$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((categories: Category[]) => {
this.categories = categories;
// Mark for check
this._changeDetectorRef.markForCheck();
});
// Get the courses
this._academyService.courses$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((courses: Course[]) => {
this.courses = this.filteredCourses = courses;
// Mark for check
this._changeDetectorRef.markForCheck();
});
// Filter the courses
combineLatest([this.filters.categorySlug$, this.filters.query$, this.filters.hideCompleted$])
.subscribe(([categorySlug, query, hideCompleted]) => {
// Reset the filtered courses
this.filteredCourses = this.courses;
// Filter by category
if ( categorySlug !== 'all' )
{
this.filteredCourses = this.filteredCourses.filter(course => course.category === categorySlug);
}
// Filter by search query
if ( query !== '' )
{
this.filteredCourses = this.filteredCourses.filter(course => course.title.toLowerCase().includes(query.toLowerCase())
|| course.description.toLowerCase().includes(query.toLowerCase())
|| course.category.toLowerCase().includes(query.toLowerCase()));
}
// Filter by completed
if ( hideCompleted )
{
this.filteredCourses = this.filteredCourses.filter(course => course.progress.completed === 0);
}
});
}
/**
* On destroy
*/
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Filter by search query
*
* @param query
*/
filterByQuery(query: string): void
{
this.filters.query$.next(query);
}
/**
* Filter by category
*
* @param change
*/
filterByCategory(change: MatSelectChange): void
{
this.filters.categorySlug$.next(change.value);
}
/**
* Show/hide completed courses
*
* @param change
*/
toggleCompleted(change: MatSlideToggleChange): void
{
this.filters.hideCompleted$.next(change.checked);
}
/**
* Track by function for ngFor loops
*
* @param index
* @param item
*/
trackByFn(index: number, item: any): any
{
return item.id || index;
}
}

View File

@@ -1,386 +0,0 @@
<div class="absolute inset-0 flex flex-col min-w-0 overflow-hidden dark:bg-gray-900">
<mat-drawer-container class="flex-auto h-full bg-transparent">
<!-- Drawer -->
<mat-drawer
class="w-60 dark:bg-gray-900"
[autoFocus]="false"
[mode]="drawerMode"
[opened]="drawerOpened"
#drawer>
<calendar-sidebar (calendarUpdated)="onCalendarUpdated($event)"></calendar-sidebar>
</mat-drawer>
<mat-drawer-content class="flex">
<!-- Main -->
<div class="flex flex-col flex-auto">
<!-- Header -->
<div class="flex flex-0 flex-wrap items-center p-4 border-b bg-card">
<button
mat-icon-button
(click)="toggleDrawer()">
<mat-icon [svgIcon]="'heroicons_outline:menu'"></mat-icon>
</button>
<div class="ml-4 text-2xl font-semibold tracking-tight whitespace-nowrap">
{{viewTitle}}
</div>
<button
class="ml-5"
mat-icon-button
(click)="previous()">
<mat-icon
class="icon-size-5"
[svgIcon]="'heroicons_solid:chevron-left'"></mat-icon>
</button>
<button
mat-icon-button
(click)="next()">
<mat-icon
class="icon-size-5"
[svgIcon]="'heroicons_solid:chevron-right'"></mat-icon>
</button>
<button
class="hidden md:inline-flex"
mat-icon-button
(click)="today()">
<mat-icon [svgIcon]="'heroicons_outline:calendar'"></mat-icon>
</button>
<div class="hidden md:block ml-auto">
<mat-form-field class="fuse-mat-dense fuse-mat-no-subscript w-30 ml-2">
<mat-select
(selectionChange)="changeView(viewChanger.value)"
[value]="view"
#viewChanger="matSelect">
<mat-option [value]="'dayGridMonth'">Month</mat-option>
<mat-option [value]="'timeGridWeek'">Week</mat-option>
<mat-option [value]="'timeGridDay'">Day</mat-option>
<mat-option [value]="'listYear'">Schedule</mat-option>
</mat-select>
</mat-form-field>
</div>
<!-- Mobile menu -->
<div class="md:hidden ml-auto">
<button
class=""
[matMenuTriggerFor]="actionsMenu"
mat-icon-button>
<mat-icon [svgIcon]="'heroicons_outline:dots-vertical'"></mat-icon>
<mat-menu #actionsMenu="matMenu">
<button
mat-menu-item
(click)="today()">
<mat-icon [svgIcon]="'heroicons_outline:calendar'"></mat-icon>
<span>Go to today</span>
</button>
<button
[matMenuTriggerFor]="actionsViewsMenu"
mat-menu-item>
<mat-icon [svgIcon]="'heroicons_outline:view-grid'"></mat-icon>
<span>View</span>
</button>
</mat-menu>
<mat-menu #actionsViewsMenu="matMenu">
<button
mat-menu-item
[disabled]="view === 'dayGridMonth'"
(click)="changeView('dayGridMonth')">
<span>Month</span>
</button>
<button
mat-menu-item
[disabled]="view === 'timeGridWeek'"
(click)="changeView('timeGridWeek')">
<span>Week</span>
</button>
<button
mat-menu-item
[disabled]="view === 'timeGridDay'"
(click)="changeView('timeGridDay')">
<span>Day</span>
</button>
<button
mat-menu-item
[disabled]="view === 'listYear'"
(click)="changeView('listYear')">
<span>Schedule</span>
</button>
</mat-menu>
</button>
</div>
</div>
<!-- FullCalendar -->
<div class="flex flex-col flex-auto">
<full-calendar
[defaultView]="view"
[events]="events"
[firstDay]="settings.startWeekOn"
[handleWindowResize]="false"
[header]="false"
[height]="'parent'"
[plugins]="calendarPlugins"
[views]="views"
(dateClick)="onDateClick($event)"
(eventClick)="onEventClick($event)"
(eventRender)="onEventRender($event)"
#fullCalendar></full-calendar>
</div>
<!-- Event panel -->
<ng-template #eventPanel>
<!-- Preview mode -->
<ng-container *ngIf="panelMode === 'view'">
<div class="flex-auto p-8">
<!-- Info -->
<div class="flex">
<mat-icon [svgIcon]="'heroicons_outline:information-circle'"></mat-icon>
<div class="flex flex-auto justify-between ml-6">
<!-- Info -->
<div>
<div class="text-3xl font-semibold tracking-tight leading-none">{{event.title || '(No title)'}}</div>
<div class="mt-0.5 text-secondary">{{event.start | date:'EEEE, MMMM d'}}</div>
<div class="text-secondary">{{recurrenceStatus}}</div>
</div>
<!-- Actions -->
<div class="flex -mt-2 -mr-2 ml-10">
<!-- Non-recurring event -->
<ng-container *ngIf="!event.recurrence">
<!-- Edit -->
<button
mat-icon-button
(click)="changeEventPanelMode('edit', 'single')">
<mat-icon [svgIcon]="'heroicons_outline:pencil-alt'"></mat-icon>
</button>
<!-- Delete -->
<button
mat-icon-button
(click)="deleteEvent(event)">
<mat-icon [svgIcon]="'heroicons_outline:trash'"></mat-icon>
</button>
</ng-container>
<!-- Recurring event -->
<ng-container *ngIf="event.recurrence">
<!-- Edit -->
<button
mat-icon-button
[matMenuTriggerFor]="editMenu">
<mat-icon [svgIcon]="'heroicons_outline:pencil-alt'"></mat-icon>
</button>
<mat-menu #editMenu="matMenu">
<button
mat-menu-item
(click)="changeEventPanelMode('edit', 'single')">
This event
</button>
<button
mat-menu-item
*ngIf="!event.isFirstInstance"
(click)="changeEventPanelMode('edit', 'future')">
This and following events
</button>
<button
mat-menu-item
(click)="changeEventPanelMode('edit', 'all')">
All events
</button>
</mat-menu>
<!-- Delete -->
<button
mat-icon-button
[matMenuTriggerFor]="deleteMenu">
<mat-icon [svgIcon]="'heroicons_outline:trash'"></mat-icon>
</button>
<mat-menu #deleteMenu="matMenu">
<button
mat-menu-item
(click)="deleteEvent(event, 'single')">
This event
</button>
<button
mat-menu-item
*ngIf="!event.isFirstInstance"
(click)="deleteEvent(event, 'future')">
This and following events
</button>
<button
mat-menu-item
(click)="deleteEvent(event, 'all')">
All events
</button>
</mat-menu>
</ng-container>
</div>
</div>
</div>
<!-- Description -->
<div
class="flex mt-6"
*ngIf="event.description">
<mat-icon [svgIcon]="'heroicons_outline:menu-alt-2'"></mat-icon>
<div class="flex-auto ml-6">{{event.description}}</div>
</div>
<!-- Calendar -->
<div class="flex mt-6">
<mat-icon [svgIcon]="'heroicons_outline:calendar'"></mat-icon>
<div class="flex flex-auto items-center ml-6">
<div
class="w-2 h-2 rounded-full"
[ngClass]="getCalendar(event.calendarId).color"></div>
<div class="ml-3 leading-none">{{getCalendar(event.calendarId).title}}</div>
</div>
</div>
</div>
</ng-container>
<!-- Add / Edit mode -->
<ng-container *ngIf="panelMode === 'add' || panelMode === 'edit'">
<form
class="flex flex-col w-full p-6 pt-8 sm:pt-10 sm:pr-8"
[formGroup]="eventForm">
<!-- Title -->
<div class="flex items-center">
<mat-icon
class="hidden sm:inline-flex mr-6"
[svgIcon]="'heroicons_outline:pencil-alt'"></mat-icon>
<mat-form-field class="fuse-mat-no-subscript flex-auto">
<input
matInput
[formControlName]="'title'"
[placeholder]="'Event title'">
</mat-form-field>
</div>
<!-- Dates -->
<div class="flex items-start mt-6">
<mat-icon
class="hidden sm:inline-flex mt-3 mr-6"
[svgIcon]="'heroicons_outline:calendar'"></mat-icon>
<div class="flex-auto">
<fuse-date-range
[formControlName]="'range'"
[dateFormat]="settings.dateFormat"
[timeRange]="!eventForm.get('allDay').value"
[timeFormat]="settings.timeFormat"></fuse-date-range>
<mat-checkbox
class="mt-4"
[color]="'primary'"
[formControlName]="'allDay'">
All day
</mat-checkbox>
</div>
</div>
<!-- Recurrence -->
<div
class="flex items-center mt-6"
*ngIf="!event.recurrence || eventEditMode !== 'single'">
<mat-icon
class="hidden sm:inline-flex mr-6 transform -scale-x-1"
[svgIcon]="'heroicons_outline:refresh'"></mat-icon>
<div
class="flex flex-auto items-center h-12 px-4 rounded-md border cursor-pointer shadow-sm border-gray-300 dark:bg-black dark:bg-opacity-5 dark:border-gray-500"
(click)="openRecurrenceDialog()">
<div class="flex-auto">
{{recurrenceStatus || 'Does not repeat'}}
</div>
</div>
</div>
<!-- Calendar -->
<div class="flex items-center mt-6">
<mat-icon
class="hidden sm:inline-flex mr-6"
[svgIcon]="'heroicons_outline:tag'"></mat-icon>
<mat-form-field class="fuse-mat-no-subscript flex-auto">
<mat-select
[formControlName]="'calendarId'"
(change)="$event.stopImmediatePropagation()">
<mat-select-trigger class="inline-flex items-center leading-none">
<span
class="w-3 h-3 rounded-full"
[ngClass]="getCalendar(eventForm.get('calendarId').value)?.color"></span>
<span class="ml-3">{{getCalendar(eventForm.get('calendarId').value)?.title}}</span>
</mat-select-trigger>
<ng-container *ngFor="let calendar of calendars">
<mat-option [value]="calendar.id">
<div class="inline-flex items-center">
<span
class="w-3 h-3 rounded-full"
[ngClass]="calendar.color"></span>
<span class="ml-3">{{calendar.title}}</span>
</div>
</mat-option>
</ng-container>
</mat-select>
</mat-form-field>
</div>
<!-- Description -->
<div class="flex items-start mt-6">
<mat-icon
class="hidden sm:inline-flex mr-6 mt-3"
[svgIcon]="'heroicons_outline:menu-alt-2'"></mat-icon>
<mat-form-field class="fuse-mat-textarea fuse-mat-no-subscript flex-auto">
<textarea
matInput
cdkTextareaAutosize
[cdkAutosizeMinRows]="1"
[formControlName]="'description'"
[placeholder]="'Event description'">
</textarea>
</mat-form-field>
</div>
<!-- Actions -->
<div class="ml-auto mt-6">
<button
class="add"
*ngIf="panelMode === 'add'"
mat-flat-button
type="button"
[color]="'primary'"
(click)="addEvent()">
Add
</button>
<button
class="save"
*ngIf="panelMode === 'edit'"
mat-flat-button
type="button"
[color]="'primary'"
(click)="updateEvent()">
Save
</button>
</div>
</form>
</ng-container>
</ng-template>
</div>
</mat-drawer-content>
</mat-drawer-container>
</div>

View File

@@ -1,121 +0,0 @@
calendar {
/* Tweak: FullCalendar CSS only height to improve resize performance */
/* With this tweak, we can disable "handleWindowResize" option of FullCalendar */
/* which disables the height calculations on window resize and increases the */
/* overall performance. */
/* This tweak only affects the Calendar app's FullCalendar. */
full-calendar {
display: flex;
flex-direction: column;
flex: 1 0 auto;
width: 100%;
height: 100%;
.fc-view-container {
display: flex;
flex-direction: column;
flex: 1 0 auto;
width: 100%;
height: 100%;
.fc-view {
/* Day grid - Month view */
/* Time grid - Week view */
/* Time grid - Day view */
&.fc-dayGridMonth-view,
&.fc-timeGridWeek-view,
&.fc-timeGridDay-view {
display: flex;
flex-direction: column;
flex: 1 0 auto;
width: 100%;
height: 100%;
> table {
display: flex;
flex-direction: column;
flex: 1 0 auto;
height: 100%;
> thead {
display: flex;
}
> tbody {
display: flex;
flex: 1 1 auto;
overflow: hidden;
> tr {
display: flex;
> td {
display: flex;
flex-direction: column;
.fc-scroller {
flex: 1 1 auto;
overflow: hidden scroll !important;
height: auto !important;
}
}
}
}
}
}
/* Day grid - Month view */
&.fc-dayGridMonth-view {
> table {
> tbody {
> tr {
> td {
.fc-scroller {
display: flex;
> .fc-day-grid {
display: flex;
flex-direction: column;
min-height: 580px;
> .fc-row {
flex: 1 0 0;
height: auto !important;
}
}
}
}
}
}
}
}
/* List - Year view */
&.fc-listYear-view {
width: 100%;
height: 100%;
.fc-scroller {
width: 100%;
height: 100% !important;
overflow: hidden !important;
}
}
}
}
}
}
/* Event panel */
.calendar-event-panel {
border-radius: 8px;
@apply shadow-2xl bg-card;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,75 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { ScrollingModule } from '@angular/cdk/scrolling';
import { MAT_DATE_FORMATS } from '@angular/material/core';
import { MatButtonModule } from '@angular/material/button';
import { MatButtonToggleModule } from '@angular/material/button-toggle';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatDialogModule } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatMenuModule } from '@angular/material/menu';
import { MatMomentDateModule } from '@angular/material-moment-adapter';
import { MatRadioModule } from '@angular/material/radio';
import { MatSelectModule } from '@angular/material/select';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatTooltipModule } from '@angular/material/tooltip';
import { FullCalendarModule } from '@fullcalendar/angular';
import { FuseDateRangeModule } from '@fuse/components/date-range';
import { SharedModule } from 'app/shared/shared.module';
import { CalendarComponent } from 'app/modules/admin/apps/calendar/calendar.component';
import { CalendarRecurrenceComponent } from 'app/modules/admin/apps/calendar/recurrence/recurrence.component';
import { CalendarSettingsComponent } from 'app/modules/admin/apps/calendar/settings/settings.component';
import { CalendarSidebarComponent } from 'app/modules/admin/apps/calendar/sidebar/sidebar.component';
import { calendarRoutes } from 'app/modules/admin/apps/calendar/calendar.routing';
@NgModule({
declarations: [
CalendarComponent,
CalendarRecurrenceComponent,
CalendarSettingsComponent,
CalendarSidebarComponent
],
imports : [
RouterModule.forChild(calendarRoutes),
ScrollingModule,
MatButtonModule,
MatButtonToggleModule,
MatCheckboxModule,
MatDatepickerModule,
MatDialogModule,
MatFormFieldModule,
MatIconModule,
MatInputModule,
MatMenuModule,
MatMomentDateModule,
MatRadioModule,
MatSelectModule,
MatSidenavModule,
MatTooltipModule,
FullCalendarModule,
FuseDateRangeModule,
SharedModule
],
providers : [
{
provide : MAT_DATE_FORMATS,
useValue: {
parse : {
dateInput: 'DD.MM.YYYY'
},
display: {
dateInput : 'DD.MM.YYYY',
monthYearLabel : 'MMM YYYY',
dateA11yLabel : 'DD.MM.YYYY',
monthYearA11yLabel: 'MMMM YYYY'
}
}
}
]
})
export class CalendarModule
{
}

View File

@@ -1,89 +0,0 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { CalendarService } from 'app/modules/admin/apps/calendar/calendar.service';
import { Calendar, CalendarSettings, CalendarWeekday } from 'app/modules/admin/apps/calendar/calendar.types';
@Injectable({
providedIn: 'root'
})
export class CalendarCalendarsResolver implements Resolve<any>
{
/**
* Constructor
*/
constructor(private _calendarService: CalendarService)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Resolver
*
* @param route
* @param state
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Calendar[]>
{
return this._calendarService.getCalendars();
}
}
@Injectable({
providedIn: 'root'
})
export class CalendarSettingsResolver implements Resolve<any>
{
/**
* Constructor
*/
constructor(private _calendarService: CalendarService)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Resolver
*
* @param route
* @param state
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<CalendarSettings>
{
return this._calendarService.getSettings();
}
}
@Injectable({
providedIn: 'root'
})
export class CalendarWeekdaysResolver implements Resolve<any>
{
/**
* Constructor
*/
constructor(private _calendarService: CalendarService)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Resolver
*
* @param route
* @param state
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<CalendarWeekday[]>
{
return this._calendarService.getWeekdays();
}
}

View File

@@ -1,23 +0,0 @@
import { Route } from '@angular/router';
import { CalendarComponent } from 'app/modules/admin/apps/calendar/calendar.component';
import { CalendarSettingsComponent } from 'app/modules/admin/apps/calendar/settings/settings.component';
import { CalendarCalendarsResolver, CalendarSettingsResolver, CalendarWeekdaysResolver } from 'app/modules/admin/apps/calendar/calendar.resolvers';
export const calendarRoutes: Route[] = [
{
path : '',
component: CalendarComponent,
resolve : {
calendars: CalendarCalendarsResolver,
settings : CalendarSettingsResolver,
weekdays : CalendarWeekdaysResolver
}
},
{
path : 'settings',
component: CalendarSettingsComponent,
resolve : {
settings: CalendarSettingsResolver
}
}
];

View File

@@ -1,475 +0,0 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject, Observable, of } from 'rxjs';
import { map, switchMap, take, tap } from 'rxjs/operators';
import { Moment } from 'moment';
import { Calendar, CalendarEvent, CalendarEventEditMode, CalendarSettings, CalendarWeekday } from 'app/modules/admin/apps/calendar/calendar.types';
@Injectable({
providedIn: 'root'
})
export class CalendarService
{
// Private
private _calendars: BehaviorSubject<Calendar[] | null> = new BehaviorSubject(null);
private _events: BehaviorSubject<CalendarEvent[] | null> = new BehaviorSubject(null);
private _loadedEventsRange: { start: Moment | null; end: Moment | null } = {
start: null,
end : null
};
private readonly _numberOfDaysToPrefetch = 60;
private _settings: BehaviorSubject<CalendarSettings | null> = new BehaviorSubject(null);
private _weekdays: BehaviorSubject<CalendarWeekday[] | null> = new BehaviorSubject(null);
/**
* Constructor
*/
constructor(private _httpClient: HttpClient)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Accessors
// -----------------------------------------------------------------------------------------------------
/**
* Getter for calendars
*/
get calendars$(): Observable<Calendar[]>
{
return this._calendars.asObservable();
}
/**
* Getter for events
*/
get events$(): Observable<CalendarEvent[]>
{
return this._events.asObservable();
}
/**
* Getter for settings
*/
get settings$(): Observable<CalendarSettings>
{
return this._settings.asObservable();
}
/**
* Getter for weekdays
*/
get weekdays$(): Observable<CalendarWeekday[]>
{
return this._weekdays.asObservable();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Get calendars
*/
getCalendars(): Observable<Calendar[]>
{
return this._httpClient.get<Calendar[]>('api/apps/calendar/calendars').pipe(
tap((response) => {
this._calendars.next(response);
})
);
}
/**
* Add calendar
*
* @param calendar
*/
addCalendar(calendar: Calendar): Observable<Calendar>
{
return this.calendars$.pipe(
take(1),
switchMap(calendars => this._httpClient.post<Calendar>('api/apps/calendar/calendars', {
calendar
}).pipe(
map((addedCalendar) => {
// Add the calendar
calendars.push(addedCalendar);
// Update the calendars
this._calendars.next(calendars);
// Return the added calendar
return addedCalendar;
})
))
);
}
/**
* Update calendar
*
* @param id
* @param calendar
*/
updateCalendar(id: string, calendar: Calendar): Observable<Calendar>
{
return this.calendars$.pipe(
take(1),
switchMap(calendars => this._httpClient.patch<Calendar>('api/apps/calendar/calendars', {
id,
calendar
}).pipe(
map((updatedCalendar) => {
// Find the index of the updated calendar
const index = calendars.findIndex(item => item.id === id);
// Update the calendar
calendars[index] = updatedCalendar;
// Update the calendars
this._calendars.next(calendars);
// Return the updated calendar
return updatedCalendar;
})
))
);
}
/**
* Delete calendar
*
* @param id
*/
deleteCalendar(id: string): Observable<any>
{
return this.calendars$.pipe(
take(1),
switchMap(calendars => this._httpClient.delete<Calendar>('api/apps/calendar/calendars', {
params: {id}
}).pipe(
map((isDeleted) => {
// Find the index of the deleted calendar
const index = calendars.findIndex(item => item.id === id);
// Delete the calendar
calendars.splice(index, 1);
// Update the calendars
this._calendars.next(calendars);
// Remove the events belong to deleted calendar
const events = this._events.value.filter(event => event.calendarId !== id);
// Update the events
this._events.next(events);
// Return the deleted status
return isDeleted;
})
))
);
}
/**
* Get events
*
* @param start
* @param end
* @param replace
*/
getEvents(start: Moment, end: Moment, replace = false): Observable<CalendarEvent[]>
{
// Set the new start date for loaded events
if ( replace || !this._loadedEventsRange.start || start.isBefore(this._loadedEventsRange.start) )
{
this._loadedEventsRange.start = start;
}
// Set the new end date for loaded events
if ( replace || !this._loadedEventsRange.end || end.isAfter(this._loadedEventsRange.end) )
{
this._loadedEventsRange.end = end;
}
// Get the events
return this._httpClient.get<CalendarEvent[]>('api/apps/calendar/events', {
params: {
start: start.toISOString(true),
end : end.toISOString(true)
}
}).pipe(
switchMap(response => this._events.pipe(
take(1),
map((events) => {
// If replace...
if ( replace )
{
// Execute the observable with the response replacing the events object
this._events.next(response);
}
// Otherwise...
else
{
// If events is null, replace it with an empty array
events = events || [];
// Execute the observable by appending the response to the current events
this._events.next([...events, ...response]);
}
// Return the response
return response;
})
))
);
}
/**
* Reload events using the loaded events range
*/
reloadEvents(): Observable<CalendarEvent[]>
{
// Get the events
return this._httpClient.get<CalendarEvent[]>('api/apps/calendar/events', {
params: {
start: this._loadedEventsRange.start.toISOString(),
end : this._loadedEventsRange.end.toISOString()
}
}).pipe(
map((response) => {
// Execute the observable with the response replacing the events object
this._events.next(response);
// Return the response
return response;
})
);
}
/**
* Prefetch future events
*
* @param end
*/
prefetchFutureEvents(end: Moment): Observable<CalendarEvent[]>
{
// Calculate the remaining prefetched days
const remainingDays = this._loadedEventsRange.end.diff(end, 'days');
// Return if remaining days is bigger than the number
// of days to prefetch. This means we were already been
// there and fetched the events data so no need for doing
// it again.
if ( remainingDays >= this._numberOfDaysToPrefetch )
{
return of([]);
}
// Figure out the start and end dates
const start = this._loadedEventsRange.end.clone().add(1, 'day');
end = this._loadedEventsRange.end.clone().add(this._numberOfDaysToPrefetch - remainingDays, 'days');
// Prefetch the events
return this.getEvents(start, end);
}
/**
* Prefetch past events
*
* @param start
*/
prefetchPastEvents(start: Moment): Observable<CalendarEvent[]>
{
// Calculate the remaining prefetched days
const remainingDays = start.diff(this._loadedEventsRange.start, 'days');
// Return if remaining days is bigger than the number
// of days to prefetch. This means we were already been
// there and fetched the events data so no need for doing
// it again.
if ( remainingDays >= this._numberOfDaysToPrefetch )
{
return of([]);
}
// Figure out the start and end dates
start = this._loadedEventsRange.start.clone().subtract(this._numberOfDaysToPrefetch - remainingDays, 'days');
const end = this._loadedEventsRange.start.clone().subtract(1, 'day');
// Prefetch the events
return this.getEvents(start, end);
}
/**
* Add event
*
* @param event
*/
addEvent(event): Observable<CalendarEvent>
{
return this.events$.pipe(
take(1),
switchMap(events => this._httpClient.post<CalendarEvent>('api/apps/calendar/event', {
event
}).pipe(
map((addedEvent) => {
// Update the events
this._events.next(events);
// Return the added event
return addedEvent;
})
))
);
}
/**
* Update event
*
* @param id
* @param event
*/
updateEvent(id: string, event): Observable<CalendarEvent>
{
return this.events$.pipe(
take(1),
switchMap(events => this._httpClient.patch<CalendarEvent>('api/apps/calendar/event', {
id,
event
}).pipe(
map((updatedEvent) => {
// Find the index of the updated event
const index = events.findIndex(item => item.id === id);
// Update the event
events[index] = updatedEvent;
// Update the events
this._events.next(events);
// Return the updated event
return updatedEvent;
})
))
);
}
/**
* Update recurring event
*
* @param event
* @param originalEvent
* @param mode
*/
updateRecurringEvent(event, originalEvent, mode: CalendarEventEditMode): Observable<boolean>
{
return this._httpClient.patch<boolean>('api/apps/calendar/recurring-event', {
event,
originalEvent,
mode
});
}
/**
* Delete event
*
* @param id
*/
deleteEvent(id: string): Observable<CalendarEvent>
{
return this.events$.pipe(
take(1),
switchMap(events => this._httpClient.delete<CalendarEvent>('api/apps/calendar/event', {params: {id}}).pipe(
map((isDeleted) => {
// Find the index of the deleted event
const index = events.findIndex(item => item.id === id);
// Delete the event
events.splice(index, 1);
// Update the events
this._events.next(events);
// Return the deleted status
return isDeleted;
})
))
);
}
/**
* Delete recurring event
*
* @param event
* @param mode
*/
deleteRecurringEvent(event, mode: CalendarEventEditMode): Observable<boolean>
{
return this._httpClient.delete<boolean>('api/apps/calendar/recurring-event', {
params: {
event: JSON.stringify(event),
mode
}
});
}
/**
* Get settings
*/
getSettings(): Observable<CalendarSettings>
{
return this._httpClient.get<CalendarSettings>('api/apps/calendar/settings').pipe(
tap((response) => {
this._settings.next(response);
})
);
}
/**
* Update settings
*/
updateSettings(settings: CalendarSettings): Observable<CalendarSettings>
{
return this.events$.pipe(
take(1),
switchMap(events => this._httpClient.patch<CalendarSettings>('api/apps/calendar/settings', {
settings
}).pipe(
map((updatedSettings) => {
// Update the settings
this._settings.next(settings);
// Get weekdays again to get them in correct order
// in case the startWeekOn setting changes
this.getWeekdays().subscribe();
// Return the updated settings
return updatedSettings;
})
))
);
}
/**
* Get weekdays
*/
getWeekdays(): Observable<CalendarWeekday[]>
{
return this._httpClient.get<CalendarWeekday[]>('api/apps/calendar/weekdays').pipe(
tap((response) => {
this._weekdays.next(response);
})
);
}
}

View File

@@ -1,47 +0,0 @@
export interface Calendar
{
id: string;
title: string;
color: string;
visible: boolean;
}
export type CalendarDrawerMode = 'over' | 'side';
export interface CalendarEvent
{
id: string;
calendarId: string;
recurringEventId: string | null;
isFirstInstance: boolean;
title: string;
description: string;
start: string | null;
end: string | null;
allDay: boolean;
recurrence: string;
}
export interface CalendarEventException
{
id: string;
eventId: string;
exdate: string;
}
export type CalendarEventPanelMode = 'view' | 'add' | 'edit';
export type CalendarEventEditMode = 'single' | 'future' | 'all';
export interface CalendarSettings
{
dateFormat: 'DD/MM/YYYY' | 'MM/DD/YYYY' | 'YYYY-MM-DD' | 'll';
timeFormat: '12' | '24';
startWeekOn: 6 | 0 | 1;
}
export interface CalendarWeekday
{
abbr: string;
label: string;
value: string;
}

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