diff --git a/npm/ng-packs/CONTRIBUTING.md b/npm/ng-packs/CONTRIBUTING.md index 76a18c04c3..2894dac56e 100644 --- a/npm/ng-packs/CONTRIBUTING.md +++ b/npm/ng-packs/CONTRIBUTING.md @@ -93,10 +93,6 @@ The following is the list of supported scopes: - **feature-management** - **permission-management** - **setting-management** -- **account-config** -- **identity-config** -- **setting-management-config** -- **tenant-management-config** There are currently a few exceptions to the "use package name" rule: diff --git a/npm/ng-packs/angular.json b/npm/ng-packs/angular.json index 87948c6f14..17f886f13a 100644 --- a/npm/ng-packs/angular.json +++ b/npm/ng-packs/angular.json @@ -329,152 +329,6 @@ } } }, - "setting-management-config": { - "projectType": "library", - "root": "packages/setting-management-config", - "sourceRoot": "packages/setting-management-config/src", - "prefix": "abp", - "architect": { - "build": { - "builder": "@angular-devkit/build-ng-packagr:build", - "options": { - "tsConfig": "packages/setting-management-config/tsconfig.lib.json", - "project": "packages/setting-management-config/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "packages/setting-management-config/tsconfig.lib.prod.json" - } - } - }, - "test": { - "builder": "@angular-builders/jest:run", - "options": { - "coverage": true, - "passWithNoTests": true - } - }, - "lint": { - "builder": "@angular-devkit/build-angular:tslint", - "options": { - "tsConfig": [ - "packages/setting-management-config/tsconfig.lib.json", - "packages/setting-management-config/tsconfig.spec.json" - ] - } - } - } - }, - "identity-config": { - "projectType": "library", - "root": "packages/identity-config", - "sourceRoot": "packages/identity-config/src", - "prefix": "abp", - "architect": { - "build": { - "builder": "@angular-devkit/build-ng-packagr:build", - "options": { - "tsConfig": "packages/identity-config/tsconfig.lib.json", - "project": "packages/identity-config/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "packages/identity-config/tsconfig.lib.prod.json" - } - } - }, - "test": { - "builder": "@angular-builders/jest:run", - "options": { - "coverage": true, - "passWithNoTests": true - } - }, - "lint": { - "builder": "@angular-devkit/build-angular:tslint", - "options": { - "tsConfig": [ - "packages/identity-config/tsconfig.lib.json", - "packages/identity-config/tsconfig.spec.json" - ], - "exclude": ["**/node_modules/**"] - } - } - } - }, - "account-config": { - "projectType": "library", - "root": "packages/account-config", - "sourceRoot": "packages/account-config/src", - "prefix": "abp", - "architect": { - "build": { - "builder": "@angular-devkit/build-ng-packagr:build", - "options": { - "tsConfig": "packages/account-config/tsconfig.lib.json", - "project": "packages/account-config/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "packages/account-config/tsconfig.lib.prod.json" - } - } - }, - "test": { - "builder": "@angular-builders/jest:run", - "options": { - "coverage": true, - "passWithNoTests": true - } - }, - "lint": { - "builder": "@angular-devkit/build-angular:tslint", - "options": { - "tsConfig": [ - "packages/account-config/tsconfig.lib.json", - "packages/account-config/tsconfig.spec.json" - ], - "exclude": ["**/node_modules/**"] - } - } - } - }, - "tenant-management-config": { - "projectType": "library", - "root": "packages/tenant-management-config", - "sourceRoot": "packages/tenant-management-config/src", - "prefix": "abp", - "architect": { - "build": { - "builder": "@angular-devkit/build-ng-packagr:build", - "options": { - "tsConfig": "packages/tenant-management-config/tsconfig.lib.json", - "project": "packages/tenant-management-config/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "packages/tenant-management-config/tsconfig.lib.prod.json" - } - } - }, - "test": { - "builder": "@angular-builders/jest:run", - "options": { - "coverage": true, - "passWithNoTests": true - } - }, - "lint": { - "builder": "@angular-devkit/build-angular:tslint", - "options": { - "tsConfig": [ - "packages/tenant-management-config/tsconfig.lib.json", - "packages/tenant-management-config/tsconfig.spec.json" - ] - } - } - } - }, "dev-app": { "projectType": "application", "schematics": { @@ -557,7 +411,7 @@ "budgets": [ { "type": "initial", - "maximumWarning": "2mb", + "maximumWarning": "1mb", "maximumError": "5mb" }, { diff --git a/npm/ng-packs/apps/dev-app/src/app/app-routing.module.ts b/npm/ng-packs/apps/dev-app/src/app/app-routing.module.ts index ac2eb4535b..be5089316a 100644 --- a/npm/ng-packs/apps/dev-app/src/app/app-routing.module.ts +++ b/npm/ng-packs/apps/dev-app/src/app/app-routing.module.ts @@ -15,21 +15,22 @@ const routes: Routes = [ }, { path: 'account', - loadChildren: () => import('./lazy-libs/account-wrapper.module').then(m => m.AccountWrapperModule), + loadChildren: () => + import('@abp/ng.account').then(m => m.AccountModule.forLazy({ redirectUrl: '/' })), }, { path: 'identity', - loadChildren: () => import('./lazy-libs/identity-wrapper.module').then(m => m.IdentityWrapperModule), + loadChildren: () => import('@abp/ng.identity').then(m => m.IdentityModule.forLazy()), }, { path: 'tenant-management', loadChildren: () => - import('./lazy-libs/tenant-management-wrapper.module').then(m => m.TenantManagementWrapperModule), + import('@abp/ng.tenant-management').then(m => m.TenantManagementModule.forLazy()), }, { path: 'setting-management', loadChildren: () => - import('./lazy-libs/setting-management-wrapper.module').then(m => m.SettingManagementWrapperModule), + import('@abp/ng.setting-management').then(m => m.SettingManagementModule.forLazy()), }, ]; diff --git a/npm/ng-packs/apps/dev-app/src/app/app.module.ts b/npm/ng-packs/apps/dev-app/src/app/app.module.ts index e00a99d499..acf8fcaaa8 100644 --- a/npm/ng-packs/apps/dev-app/src/app/app.module.ts +++ b/npm/ng-packs/apps/dev-app/src/app/app.module.ts @@ -1,4 +1,9 @@ +import { AccountConfigModule } from '@abp/ng.account/config'; import { CoreModule } from '@abp/ng.core'; +import { IdentityConfigModule } from '@abp/ng.identity/config'; +import { SettingManagementConfigModule } from '@abp/ng.setting-management/config'; +import { TenantManagementConfigModule } from '@abp/ng.tenant-management/config'; +import { ThemeSharedModule } from '@abp/ng.theme.shared'; import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; @@ -8,11 +13,6 @@ import { environment } from '../environments/environment'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { SharedModule } from './shared/shared.module'; -import { ThemeSharedModule } from '@abp/ng.theme.shared'; -import { AccountConfigModule } from '@abp/ng.account.config'; -import { IdentityConfigModule } from '@abp/ng.identity.config'; -import { TenantManagementConfigModule } from '@abp/ng.tenant-management.config'; -import { SettingManagementConfigModule } from '@abp/ng.setting-management.config'; const LOGGERS = [NgxsLoggerPluginModule.forRoot({ disabled: false })]; @@ -27,10 +27,10 @@ const LOGGERS = [NgxsLoggerPluginModule.forRoot({ disabled: false })]; skipGetAppConfiguration: false, }), ThemeSharedModule.forRoot(), - AccountConfigModule.forRoot({ redirectUrl: '/' }), - IdentityConfigModule, - TenantManagementConfigModule, - SettingManagementConfigModule, + AccountConfigModule.forRoot(), + IdentityConfigModule.forRoot(), + TenantManagementConfigModule.forRoot(), + SettingManagementConfigModule.forRoot(), NgxsModule.forRoot(), SharedModule, ...(environment.production ? [] : LOGGERS), diff --git a/npm/ng-packs/apps/dev-app/src/app/lazy-libs/account-wrapper.module.ts b/npm/ng-packs/apps/dev-app/src/app/lazy-libs/account-wrapper.module.ts deleted file mode 100644 index 7130bb1a1a..0000000000 --- a/npm/ng-packs/apps/dev-app/src/app/lazy-libs/account-wrapper.module.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { NgModule } from '@angular/core'; -import { AccountModule } from '@abp/ng.account'; - -@NgModule({ - imports: [AccountModule], -}) -export class AccountWrapperModule {} diff --git a/npm/ng-packs/apps/dev-app/src/app/lazy-libs/identity-wrapper.module.ts b/npm/ng-packs/apps/dev-app/src/app/lazy-libs/identity-wrapper.module.ts deleted file mode 100644 index fed1dcd1ec..0000000000 --- a/npm/ng-packs/apps/dev-app/src/app/lazy-libs/identity-wrapper.module.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { NgModule } from '@angular/core'; -import { IdentityModule } from '@abp/ng.identity'; - -@NgModule({ - imports: [IdentityModule], -}) -export class IdentityWrapperModule {} diff --git a/npm/ng-packs/apps/dev-app/src/app/lazy-libs/setting-management-wrapper.module.ts b/npm/ng-packs/apps/dev-app/src/app/lazy-libs/setting-management-wrapper.module.ts deleted file mode 100644 index e5f3db8878..0000000000 --- a/npm/ng-packs/apps/dev-app/src/app/lazy-libs/setting-management-wrapper.module.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { NgModule } from '@angular/core'; -import { SettingManagementModule } from '@abp/ng.setting-management'; - -@NgModule({ - imports: [SettingManagementModule], -}) -export class SettingManagementWrapperModule {} diff --git a/npm/ng-packs/apps/dev-app/src/app/lazy-libs/tenant-management-wrapper.module.ts b/npm/ng-packs/apps/dev-app/src/app/lazy-libs/tenant-management-wrapper.module.ts deleted file mode 100644 index 63eabda505..0000000000 --- a/npm/ng-packs/apps/dev-app/src/app/lazy-libs/tenant-management-wrapper.module.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { NgModule } from '@angular/core'; -import { TenantManagementModule } from '@abp/ng.tenant-management'; - -@NgModule({ - imports: [TenantManagementModule], -}) -export class TenantManagementWrapperModule {} diff --git a/npm/ng-packs/package.json b/npm/ng-packs/package.json index e42cf9ecc2..37476c4fd8 100644 --- a/npm/ng-packs/package.json +++ b/npm/ng-packs/package.json @@ -24,16 +24,12 @@ }, "devDependencies": { "@abp/ng.account": "~2.9.0", - "@abp/ng.account.config": "~2.9.0", "@abp/ng.core": "~2.9.0", "@abp/ng.feature-management": "~2.9.0", "@abp/ng.identity": "~2.9.0", - "@abp/ng.identity.config": "~2.9.0", "@abp/ng.permission-management": "~2.9.0", "@abp/ng.setting-management": "~2.9.0", - "@abp/ng.setting-management.config": "~2.9.0", "@abp/ng.tenant-management": "~2.9.0", - "@abp/ng.tenant-management.config": "~2.9.0", "@abp/ng.theme.basic": "~2.9.0", "@abp/ng.theme.shared": "~2.9.0", "@abp/utils": "^2.9.0", diff --git a/npm/ng-packs/packages/account-config/README.md b/npm/ng-packs/packages/account-config/README.md deleted file mode 100644 index 8212588145..0000000000 --- a/npm/ng-packs/packages/account-config/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# @abp/ng.account.config - -[docs.abp.io](https://docs.abp.io) diff --git a/npm/ng-packs/packages/account-config/jest.config.js b/npm/ng-packs/packages/account-config/jest.config.js deleted file mode 100644 index 97cd0af1aa..0000000000 --- a/npm/ng-packs/packages/account-config/jest.config.js +++ /dev/null @@ -1,6 +0,0 @@ -const jestConfig = require('../../jest.config'); - -module.exports = { - ...jestConfig, - name: 'account-config', -}; diff --git a/npm/ng-packs/packages/account-config/ng-package.json b/npm/ng-packs/packages/account-config/ng-package.json deleted file mode 100644 index 75c8479ea8..0000000000 --- a/npm/ng-packs/packages/account-config/ng-package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", - "dest": "../../dist/account-config", - "lib": { - "entryFile": "src/public-api.ts" - } -} diff --git a/npm/ng-packs/packages/account-config/package.json b/npm/ng-packs/packages/account-config/package.json deleted file mode 100644 index 2b7882bcc2..0000000000 --- a/npm/ng-packs/packages/account-config/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "@abp/ng.account.config", - "version": "2.9.0", - "homepage": "https://abp.io", - "repository": { - "type": "git", - "url": "https://github.com/abpframework/abp.git" - }, - "publishConfig": { - "access": "public" - } -} diff --git a/npm/ng-packs/packages/account-config/src/lib/account-config.module.ts b/npm/ng-packs/packages/account-config/src/lib/account-config.module.ts deleted file mode 100644 index 997bcbac81..0000000000 --- a/npm/ng-packs/packages/account-config/src/lib/account-config.module.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { CoreModule, noop } from '@abp/ng.core'; -import { ThemeSharedModule } from '@abp/ng.theme.shared'; -import { APP_INITIALIZER, InjectionToken, ModuleWithProviders, NgModule } from '@angular/core'; -import { AccountConfigService } from './services/account-config.service'; - -export interface AccountConfigOptions { - redirectUrl?: string; -} - -export function accountOptionsFactory(options: AccountConfigOptions) { - return { - redirectUrl: '/', - ...options, - }; -} - -export const ACCOUNT_OPTIONS = new InjectionToken('ACCOUNT_OPTIONS'); - -@NgModule({ - imports: [CoreModule, ThemeSharedModule], - providers: [{ provide: APP_INITIALIZER, multi: true, deps: [AccountConfigService], useFactory: noop }], -}) -export class AccountConfigModule { - static forRoot(options = {} as AccountConfigOptions): ModuleWithProviders { - return { - ngModule: AccountConfigModule, - providers: [ - { provide: ACCOUNT_OPTIONS, useValue: options }, - { - provide: 'ACCOUNT_OPTIONS', - useFactory: accountOptionsFactory, - deps: [ACCOUNT_OPTIONS], - }, - ], - }; - } -} diff --git a/npm/ng-packs/packages/account-config/src/lib/services/account-config.service.ts b/npm/ng-packs/packages/account-config/src/lib/services/account-config.service.ts deleted file mode 100644 index d94935c071..0000000000 --- a/npm/ng-packs/packages/account-config/src/lib/services/account-config.service.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { addAbpRoutes, eLayoutType } from '@abp/ng.core'; -import { Injectable } from '@angular/core'; -import { eAccountRouteNames } from '@abp/ng.account'; - -@Injectable({ - providedIn: 'root', -}) -export class AccountConfigService { - constructor() { - addAbpRoutes({ - name: eAccountRouteNames.Account, - path: 'account', - invisible: true, - layout: eLayoutType.application, - children: [ - { path: 'login', name: eAccountRouteNames.Login, order: 1 }, - { path: 'register', name: eAccountRouteNames.Register, order: 2 }, - { path: 'manage-profile', name: eAccountRouteNames.ManageProfile, order: 3 }, - ], - }); - } -} diff --git a/npm/ng-packs/packages/account-config/src/public-api.ts b/npm/ng-packs/packages/account-config/src/public-api.ts deleted file mode 100644 index 83270614c0..0000000000 --- a/npm/ng-packs/packages/account-config/src/public-api.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './lib/services/account-config.service'; -export * from './lib/account-config.module'; diff --git a/npm/ng-packs/packages/account-config/tsconfig.lib.json b/npm/ng-packs/packages/account-config/tsconfig.lib.json deleted file mode 100644 index 4f48db2e46..0000000000 --- a/npm/ng-packs/packages/account-config/tsconfig.lib.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": "../../tsconfig.prod.json", - "compilerOptions": { - "outDir": "../../out-tsc/lib", - "target": "es2015", - "declaration": true, - "inlineSources": true, - "types": [], - "lib": ["dom", "es2018"] - }, - "angularCompilerOptions": { - "skipTemplateCodegen": true, - "strictMetadataEmit": true, - "fullTemplateTypeCheck": true, - "strictInjectionParameters": true, - "enableResourceInlining": true - }, - "exclude": ["src/test.ts", "**/*.spec.ts"] -} diff --git a/npm/ng-packs/packages/account-config/tsconfig.lib.prod.json b/npm/ng-packs/packages/account-config/tsconfig.lib.prod.json deleted file mode 100644 index b1d501abbc..0000000000 --- a/npm/ng-packs/packages/account-config/tsconfig.lib.prod.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "./tsconfig.lib.json", - "angularCompilerOptions": { - "enableIvy": false - } -} \ No newline at end of file diff --git a/npm/ng-packs/packages/account-config/tsconfig.spec.json b/npm/ng-packs/packages/account-config/tsconfig.spec.json deleted file mode 100644 index 02f312c262..0000000000 --- a/npm/ng-packs/packages/account-config/tsconfig.spec.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../../tsconfig.prod.json", - "compilerOptions": { - "emitDecoratorMetadata": true, - "esModuleInterop": true, - "outDir": "../../dist/out-tsc", - "module": "commonjs", - "types": ["jest", "node"] - }, - "include": ["**/*.spec.ts", "**/*.d.ts"] -} diff --git a/npm/ng-packs/packages/account-config/tslint.json b/npm/ng-packs/packages/account-config/tslint.json deleted file mode 100644 index 8c3919ea62..0000000000 --- a/npm/ng-packs/packages/account-config/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "abp", - "camelCase" - ], - "component-selector": [ - true, - "element", - "abp", - "kebab-case" - ] - } -} diff --git a/npm/ng-packs/packages/account/config/ng-package.json b/npm/ng-packs/packages/account/config/ng-package.json new file mode 100644 index 0000000000..8bc97815a5 --- /dev/null +++ b/npm/ng-packs/packages/account/config/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../dist/account/config", + "lib": { + "entryFile": "src/public-api.ts" + } +} diff --git a/npm/ng-packs/packages/account/config/src/account-config.module.ts b/npm/ng-packs/packages/account/config/src/account-config.module.ts new file mode 100644 index 0000000000..60b2f8a6ef --- /dev/null +++ b/npm/ng-packs/packages/account/config/src/account-config.module.ts @@ -0,0 +1,12 @@ +import { ModuleWithProviders, NgModule } from '@angular/core'; +import { ACCOUNT_ROUTE_PROVIDERS } from './providers/route.provider'; + +@NgModule() +export class AccountConfigModule { + static forRoot(): ModuleWithProviders { + return { + ngModule: AccountConfigModule, + providers: [ACCOUNT_ROUTE_PROVIDERS], + }; + } +} diff --git a/npm/ng-packs/packages/account/config/src/enums/index.ts b/npm/ng-packs/packages/account/config/src/enums/index.ts new file mode 100644 index 0000000000..3bda94b078 --- /dev/null +++ b/npm/ng-packs/packages/account/config/src/enums/index.ts @@ -0,0 +1 @@ +export * from './route-names'; diff --git a/npm/ng-packs/packages/account/src/lib/enums/route-names.ts b/npm/ng-packs/packages/account/config/src/enums/route-names.ts similarity index 100% rename from npm/ng-packs/packages/account/src/lib/enums/route-names.ts rename to npm/ng-packs/packages/account/config/src/enums/route-names.ts diff --git a/npm/ng-packs/packages/account/config/src/providers/index.ts b/npm/ng-packs/packages/account/config/src/providers/index.ts new file mode 100644 index 0000000000..fe08efba8c --- /dev/null +++ b/npm/ng-packs/packages/account/config/src/providers/index.ts @@ -0,0 +1 @@ +export * from './route.provider'; diff --git a/npm/ng-packs/packages/account/config/src/providers/route.provider.ts b/npm/ng-packs/packages/account/config/src/providers/route.provider.ts new file mode 100644 index 0000000000..2deb086b10 --- /dev/null +++ b/npm/ng-packs/packages/account/config/src/providers/route.provider.ts @@ -0,0 +1,39 @@ +import { eLayoutType, RoutesService } from '@abp/ng.core'; +import { APP_INITIALIZER } from '@angular/core'; +import { eAccountRouteNames } from '../enums/route-names'; + +export const ACCOUNT_ROUTE_PROVIDERS = [ + { provide: APP_INITIALIZER, useFactory: configureRoutes, deps: [RoutesService], multi: true }, +]; + +export function configureRoutes(routes: RoutesService) { + return () => { + routes.add([ + { + path: '/account', + name: eAccountRouteNames.Account, + invisible: true, + layout: eLayoutType.application, + order: 1, + }, + { + path: '/account/login', + name: eAccountRouteNames.Login, + parentName: eAccountRouteNames.Account, + order: 1, + }, + { + path: '/account/register', + name: eAccountRouteNames.Register, + parentName: eAccountRouteNames.Account, + order: 2, + }, + { + path: '/account/manage-profile', + name: eAccountRouteNames.ManageProfile, + parentName: eAccountRouteNames.Account, + order: 3, + }, + ]); + }; +} diff --git a/npm/ng-packs/packages/account/config/src/public-api.ts b/npm/ng-packs/packages/account/config/src/public-api.ts new file mode 100644 index 0000000000..cb4f854a64 --- /dev/null +++ b/npm/ng-packs/packages/account/config/src/public-api.ts @@ -0,0 +1,3 @@ +export * from './account-config.module'; +export * from './enums'; +export * from './providers'; diff --git a/npm/ng-packs/packages/account/ng-package.json b/npm/ng-packs/packages/account/ng-package.json index 0d03a54224..9e94619264 100644 --- a/npm/ng-packs/packages/account/ng-package.json +++ b/npm/ng-packs/packages/account/ng-package.json @@ -4,5 +4,5 @@ "lib": { "entryFile": "src/public-api.ts" }, - "whitelistedNonPeerDependencies": ["@abp/ng.theme.shared", "@abp/ng.account.config"] + "whitelistedNonPeerDependencies": ["@abp/ng.theme.shared"] } diff --git a/npm/ng-packs/packages/account/package.json b/npm/ng-packs/packages/account/package.json index 1dc55cdfd1..254b6031a1 100644 --- a/npm/ng-packs/packages/account/package.json +++ b/npm/ng-packs/packages/account/package.json @@ -7,7 +7,6 @@ "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.account.config": "~2.9.0", "@abp/ng.theme.shared": "~2.9.0" }, "publishConfig": { diff --git a/npm/ng-packs/packages/account/src/lib/account.module.ts b/npm/ng-packs/packages/account/src/lib/account.module.ts index 93a496f8b5..0ee605a75a 100644 --- a/npm/ng-packs/packages/account/src/lib/account.module.ts +++ b/npm/ng-packs/packages/account/src/lib/account.module.ts @@ -1,9 +1,10 @@ -import { CoreModule } from '@abp/ng.core'; +import { CoreModule, LazyModuleFactory } from '@abp/ng.core'; import { ThemeSharedModule } from '@abp/ng.theme.shared'; -import { NgModule, Provider } from '@angular/core'; +import { ModuleWithProviders, NgModule, NgModuleFactory } from '@angular/core'; import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; import { NgxValidateCoreModule } from '@ngx-validate/core'; import { AccountRoutingModule } from './account-routing.module'; +import { AuthWrapperComponent } from './components/auth-wrapper/auth-wrapper.component'; import { ChangePasswordComponent } from './components/change-password/change-password.component'; import { LoginComponent } from './components/login/login.component'; import { ManageProfileComponent } from './components/manage-profile/manage-profile.component'; @@ -11,8 +12,8 @@ import { PersonalSettingsComponent } from './components/personal-settings/person import { RegisterComponent } from './components/register/register.component'; import { TenantBoxComponent } from './components/tenant-box/tenant-box.component'; import { Options } from './models/options'; -import { ACCOUNT_OPTIONS, optionsFactory } from './tokens/options.token'; -import { AuthWrapperComponent } from './components/auth-wrapper/auth-wrapper.component'; +import { ACCOUNT_OPTIONS } from './tokens/options.token'; +import { accountOptionsFactory } from './utils/factory-utils'; @NgModule({ declarations: [ @@ -33,4 +34,22 @@ import { AuthWrapperComponent } from './components/auth-wrapper/auth-wrapper.com ], exports: [], }) -export class AccountModule {} +export class AccountModule { + static forChild(options: Options): ModuleWithProviders { + return { + ngModule: AccountModule, + providers: [ + { provide: ACCOUNT_OPTIONS, useValue: options }, + { + provide: 'ACCOUNT_OPTIONS', + useFactory: accountOptionsFactory, + deps: [ACCOUNT_OPTIONS], + }, + ], + }; + } + + static forLazy(options: Options): NgModuleFactory { + return new LazyModuleFactory(AccountModule.forChild(options)); + } +} diff --git a/npm/ng-packs/packages/account/src/lib/enums/index.ts b/npm/ng-packs/packages/account/src/lib/enums/index.ts index 5ac9b6e8fa..07635cbbc8 100644 --- a/npm/ng-packs/packages/account/src/lib/enums/index.ts +++ b/npm/ng-packs/packages/account/src/lib/enums/index.ts @@ -1,2 +1 @@ export * from './components'; -export * from './route-names'; diff --git a/npm/ng-packs/packages/account/src/lib/tokens/options.token.ts b/npm/ng-packs/packages/account/src/lib/tokens/options.token.ts index 92b142b09c..a7f29c43b9 100644 --- a/npm/ng-packs/packages/account/src/lib/tokens/options.token.ts +++ b/npm/ng-packs/packages/account/src/lib/tokens/options.token.ts @@ -1,11 +1,4 @@ import { InjectionToken } from '@angular/core'; import { Options } from '../models/options'; -export function optionsFactory(options: Options) { - return { - redirectUrl: '/', - ...options, - }; -} - -export const ACCOUNT_OPTIONS = new InjectionToken('ACCOUNT_OPTIONS'); +export const ACCOUNT_OPTIONS = new InjectionToken('ACCOUNT_OPTIONS'); diff --git a/npm/ng-packs/packages/account/src/lib/utils/factory-utils.ts b/npm/ng-packs/packages/account/src/lib/utils/factory-utils.ts new file mode 100644 index 0000000000..3e7c88f795 --- /dev/null +++ b/npm/ng-packs/packages/account/src/lib/utils/factory-utils.ts @@ -0,0 +1,8 @@ +import { Options } from '../models/options'; + +export function accountOptionsFactory(options: Options) { + return { + redirectUrl: '/', + ...options, + }; +} diff --git a/npm/ng-packs/packages/account/src/lib/utils/index.ts b/npm/ng-packs/packages/account/src/lib/utils/index.ts new file mode 100644 index 0000000000..ed99d0aaa4 --- /dev/null +++ b/npm/ng-packs/packages/account/src/lib/utils/index.ts @@ -0,0 +1 @@ +export * from './factory-utils'; diff --git a/npm/ng-packs/packages/core/src/lib/actions/config.actions.ts b/npm/ng-packs/packages/core/src/lib/actions/config.actions.ts index 8fd11bd2cb..535f3f3b81 100644 --- a/npm/ng-packs/packages/core/src/lib/actions/config.actions.ts +++ b/npm/ng-packs/packages/core/src/lib/actions/config.actions.ts @@ -1,23 +1,9 @@ -import { ABP } from '../models/common'; import { Config } from '../models/config'; -export class PatchRouteByName { - static readonly type = '[Config] Patch Route By Name'; - constructor(public name: string, public newValue: Partial) {} -} - export class GetAppConfiguration { static readonly type = '[Config] Get App Configuration'; } -/** - * @see usage: https://github.com/abpframework/abp/pull/2425#issue-355018812 - */ -export class AddRoute { - static readonly type = '[Config] Add Route'; - constructor(public payload: Omit) {} -} - export class SetEnvironment { static readonly type = '[Config] Set Environment'; constructor(public environment: Config.Environment) {} diff --git a/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts b/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts index c07f11580f..5455187086 100644 --- a/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts +++ b/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts @@ -1,13 +1,14 @@ -import { Component, OnDestroy, Type } from '@angular/core'; -import { ActivatedRoute, NavigationEnd, Router, UrlSegment } from '@angular/router'; +import { Component, Injector, OnDestroy, Type } from '@angular/core'; +import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; import { Store } from '@ngxs/store'; -import snq from 'snq'; import { eLayoutType } from '../enums/common'; -import { ABP } from '../models/common'; +import { ABP } from '../models'; import { ReplaceableComponents } from '../models/replaceable-components'; -import { ConfigState } from '../states/config.state'; +import { RoutesService } from '../services/routes.service'; import { ReplaceableComponentsState } from '../states/replaceable-components.state'; +import { getRoutePath } from '../utils/route-utils'; import { takeUntilDestroy } from '../utils/rxjs-utils'; +import { TreeNode } from '../utils/tree-utils'; @Component({ selector: 'abp-dynamic-layout', @@ -22,23 +23,37 @@ import { takeUntilDestroy } from '../utils/rxjs-utils'; export class DynamicLayoutComponent implements OnDestroy { layout: Type; - constructor(private router: Router, private route: ActivatedRoute, private store: Store) { - const { routes } = this.store.selectSnapshot(ConfigState.getAll); + constructor( + injector: Injector, + private route: ActivatedRoute, + private routes: RoutesService, + private store: Store, + ) { + const router = injector.get(Router); + const layouts = { + application: this.getComponent('Theme.ApplicationLayoutComponent'), + account: this.getComponent('Theme.AccountLayoutComponent'), + empty: this.getComponent('Theme.EmptyLayoutComponent'), + }; router.events.pipe(takeUntilDestroy(this)).subscribe(event => { if (event instanceof NavigationEnd) { - const segments = snq(() => router.parseUrl(event.url).root.children.primary.segments, [ - { path: router.url.replace('/', '') }, - ] as any); + let expectedLayout = (this.route.snapshot.data || {}).layout; + const path = getRoutePath(router); - const layouts = { - application: this.getComponent('Theme.ApplicationLayoutComponent'), - account: this.getComponent('Theme.AccountLayoutComponent'), - empty: this.getComponent('Theme.EmptyLayoutComponent'), - }; + if (!expectedLayout) { + let node = { parent: this.routes.search({ path }) } as TreeNode; + while (node.parent) { + node = node.parent; - const expectedLayout = - (this.route.snapshot.data || {}).layout || findLayout(segments, routes); + if (node.layout) { + expectedLayout = node.layout; + break; + } + } + } + + if (!expectedLayout) expectedLayout = eLayoutType.empty; this.layout = layouts[expectedLayout].component; } @@ -51,27 +66,3 @@ export class DynamicLayoutComponent implements OnDestroy { ngOnDestroy() {} } - -function findLayout(segments: UrlSegment[], routes: ABP.FullRoute[]): eLayoutType { - let layout = eLayoutType.empty; - - const route = routes - .reduce((acc, val) => (val.wrapper ? [...acc, ...val.children] : [...acc, val]), []) - .find(r => r.path === segments[0].path); - - if (route) { - if (route.layout) { - layout = route.layout; - } - - if (route.children && route.children.length && segments.length > 1) { - const child = route.children.find(c => c.path === segments[1].path); - - if (child && child.layout) { - layout = child.layout; - } - } - } - - return layout; -} diff --git a/npm/ng-packs/packages/core/src/lib/models/common.ts b/npm/ng-packs/packages/core/src/lib/models/common.ts index 08f7681b5e..79e3d3f52b 100644 --- a/npm/ng-packs/packages/core/src/lib/models/common.ts +++ b/npm/ng-packs/packages/core/src/lib/models/common.ts @@ -1,4 +1,4 @@ -import { EventEmitter } from '@angular/core'; +import { EventEmitter, Type } from '@angular/core'; import { Router } from '@angular/router'; import { Subject } from 'rxjs'; import { eLayoutType } from '../enums/common'; @@ -30,21 +30,25 @@ export namespace ABP { maxResultCount?: number; } - export interface Route { - children?: Route[]; - invisible?: boolean; - layout?: eLayoutType; + export interface Node { name: string; - order?: number; parentName?: string; + order?: number; + invisible?: boolean; + } + + export interface Nav extends Node { path: string; requiredPolicy?: string; + } + + export interface Route extends Nav { + layout?: eLayoutType; iconClass?: string; } - export interface FullRoute extends Route { - url?: string; - wrapper?: boolean; + export interface Tab extends Nav { + component: Type; } export interface BasicItem { diff --git a/npm/ng-packs/packages/core/src/lib/models/config.ts b/npm/ng-packs/packages/core/src/lib/models/config.ts index 2169f75643..d328cfe5b5 100644 --- a/npm/ng-packs/packages/core/src/lib/models/config.ts +++ b/npm/ng-packs/packages/core/src/lib/models/config.ts @@ -4,11 +4,7 @@ import { ApplicationConfiguration } from './application-configuration'; import { ABP } from './common'; export namespace Config { - export type State = ApplicationConfiguration.Response & - ABP.Root & { environment: Environment } & { - routes: ABP.FullRoute[]; - flattedRoutes: ABP.FullRoute[]; - }; + export type State = ApplicationConfiguration.Response & ABP.Root & { environment: Environment }; export interface Environment { application: Application; diff --git a/npm/ng-packs/packages/core/src/lib/plugins/config.plugin.ts b/npm/ng-packs/packages/core/src/lib/plugins/config.plugin.ts index b2afab6072..9ca46c9277 100644 --- a/npm/ng-packs/packages/core/src/lib/plugins/config.plugin.ts +++ b/npm/ng-packs/packages/core/src/lib/plugins/config.plugin.ts @@ -1,5 +1,4 @@ import { Inject, Injectable, InjectionToken } from '@angular/core'; -import { Router, Routes } from '@angular/router'; import { actionMatcher, InitState, @@ -8,10 +7,7 @@ import { setValue, UpdateState, } from '@ngxs/store'; -import clone from 'just-clone'; -import snq from 'snq'; import { ABP } from '../models/common'; -import { getAbpRoutes, organizeRoutes } from '../utils/route-utils'; export const NGXS_CONFIG_PLUGIN_OPTIONS = new InjectionToken('NGXS_CONFIG_PLUGIN_OPTIONS'); @@ -19,27 +15,16 @@ export const NGXS_CONFIG_PLUGIN_OPTIONS = new InjectionToken('NGXS_CONFIG_PLUGIN export class ConfigPlugin implements NgxsPlugin { private initialized = false; - constructor( - @Inject(NGXS_CONFIG_PLUGIN_OPTIONS) private options: ABP.Root, - private router: Router, - ) {} + constructor(@Inject(NGXS_CONFIG_PLUGIN_OPTIONS) private options: ABP.Root) {} handle(state: any, event: any, next: NgxsNextPluginFn) { const matches = actionMatcher(event); const isInitAction = matches(InitState) || matches(UpdateState); if (isInitAction && !this.initialized) { - const transformedRoutes = transformRoutes(this.router.config); - let { routes, wrappers } = transformedRoutes; - wrappers = reduceWrappers(wrappers); - - routes = organizeRoutes(routes, wrappers); - const flattedRoutes = flatRoutes(clone(routes)); state = setValue(state, 'ConfigState', { ...(state.ConfigState && { ...state.ConfigState }), ...this.options, - routes, - flattedRoutes, }); this.initialized = true; @@ -48,84 +33,3 @@ export class ConfigPlugin implements NgxsPlugin { return next(state, event); } } - -function transformRoutes(routes: Routes = [], wrappers: ABP.FullRoute[] = []): any { - const abpRoutes = [...getAbpRoutes()]; - - wrappers = abpRoutes.filter(ar => ar.wrapper); - const transformed = [] as ABP.FullRoute[]; - routes - .filter(route => route.component || route.loadChildren) - .forEach(route => { - const abpPackage = abpRoutes.find( - abp => abp.path.toLowerCase() === route.path.toLowerCase() && !abp.wrapper, - ); - - const { length } = transformed; - - if (abpPackage) { - transformed.push(abpPackage); - } - - if (transformed.length === length && (route.data || {}).routes) { - transformed.push({ - ...route.data.routes, - path: route.path, - name: snq(() => route.data.routes.name, route.path), - children: route.data.routes.children || [], - } as ABP.FullRoute); - } - }); - - return { routes: setUrls(transformed), wrappers }; -} - -function setUrls(routes: ABP.FullRoute[], parentUrl?: string): ABP.FullRoute[] { - if (parentUrl) { - // recursive block - return routes.map(route => ({ - ...route, - url: `${parentUrl}/${route.path}`, - ...(route.children && - route.children.length && { - children: setUrls(route.children, `${parentUrl}/${route.path}`), - }), - })); - } - - return routes.map(route => ({ - ...route, - url: `/${route.path}`, - ...(route.children && - route.children.length && { - children: setUrls(route.children, `/${route.path}`), - }), - })); -} - -function flatRoutes(routes: ABP.FullRoute[]): ABP.FullRoute[] { - const flat = (r: ABP.FullRoute[]) => { - return r.reduce((acc, val) => { - let value: ABP.FullRoute[] = [val]; - if (val.children) { - val.children = val.children.map(child => ({ ...child, parentName: val.name })); - value = [val, ...flat(val.children)]; - } - - return [...acc, ...value]; - }, []); - }; - - return flat(routes); -} - -function reduceWrappers(wrappers: ABP.FullRoute[] = []) { - const existingWrappers = new Set(); - - return wrappers.filter(wrapper => { - if (existingWrappers.has(wrapper.name)) return false; - - existingWrappers.add(wrapper.name); - return true; - }); -} diff --git a/npm/ng-packs/packages/core/src/lib/services/config-state.service.ts b/npm/ng-packs/packages/core/src/lib/services/config-state.service.ts index cf97e42b0f..fe10f9ddf0 100644 --- a/npm/ng-packs/packages/core/src/lib/services/config-state.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/config-state.service.ts @@ -1,11 +1,6 @@ import { Injectable } from '@angular/core'; import { Store } from '@ngxs/store'; -import { - AddRoute, - GetAppConfiguration, - PatchRouteByName, - SetEnvironment, -} from '../actions/config.actions'; +import { GetAppConfiguration, SetEnvironment } from '../actions/config.actions'; import { ConfigState } from '../states'; @Injectable({ @@ -30,10 +25,6 @@ export class ConfigStateService { return this.store.selectSnapshot(ConfigState.getDeep(...args)); } - getRoute(...args: Parameters) { - return this.store.selectSnapshot(ConfigState.getRoute(...args)); - } - getApiUrl(...args: Parameters) { return this.store.selectSnapshot(ConfigState.getApiUrl(...args)); } @@ -58,14 +49,6 @@ export class ConfigStateService { return this.store.dispatch(new GetAppConfiguration()); } - dispatchPatchRouteByName(...args: ConstructorParameters) { - return this.store.dispatch(new PatchRouteByName(...args)); - } - - dispatchAddRoute(...args: ConstructorParameters) { - return this.store.dispatch(new AddRoute(...args)); - } - dispatchSetEnvironment(...args: ConstructorParameters) { return this.store.dispatch(new SetEnvironment(...args)); } diff --git a/npm/ng-packs/packages/core/src/lib/services/index.ts b/npm/ng-packs/packages/core/src/lib/services/index.ts index f01dc876de..064dcbd35e 100644 --- a/npm/ng-packs/packages/core/src/lib/services/index.ts +++ b/npm/ng-packs/packages/core/src/lib/services/index.ts @@ -9,5 +9,6 @@ export * from './localization.service'; export * from './profile-state.service'; export * from './profile.service'; export * from './rest.service'; +export * from './routes.service'; export * from './session-state.service'; export * from './track-by.service'; diff --git a/npm/ng-packs/packages/core/src/lib/services/routes.service.ts b/npm/ng-packs/packages/core/src/lib/services/routes.service.ts new file mode 100644 index 0000000000..4b9f3e7899 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/services/routes.service.ts @@ -0,0 +1,144 @@ +import { Injectable } from '@angular/core'; +import { Store } from '@ngxs/store'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { ABP } from '../models/common'; +import { ConfigState } from '../states/config.state'; +import { pushValueTo } from '../utils/array-utils'; +import { BaseTreeNode, createTreeFromList, TreeNode } from '../utils/tree-utils'; + +export abstract class AbstractTreeService { + abstract id: string; + abstract parentId: string; + abstract hide: (item: T) => boolean; + abstract sort: (a: T, b: T) => number; + + private _flat$ = new BehaviorSubject([]); + private _tree$ = new BehaviorSubject[]>([]); + private _visible$ = new BehaviorSubject[]>([]); + + get flat(): T[] { + return this._flat$.value; + } + + get flat$(): Observable { + return this._flat$.asObservable(); + } + + get tree(): TreeNode[] { + return this._tree$.value; + } + + get tree$(): Observable[]> { + return this._tree$.asObservable(); + } + + get visible(): TreeNode[] { + return this._visible$.value; + } + + get visible$(): Observable[]> { + return this._visible$.asObservable(); + } + + protected createTree(items: T[]): TreeNode[] { + return createTreeFromList>( + items, + item => item[this.id], + item => item[this.parentId], + item => BaseTreeNode.create(item), + ); + } + + private filterWith(setOrMap: Set | Map): T[] { + return this._flat$.value.filter( + item => !setOrMap.has(item[this.id]) && !setOrMap.has(item[this.parentId]), + ); + } + + private publish(flatItems: T[], visibleItems: T[]): T[] { + this._flat$.next(flatItems); + this._tree$.next(this.createTree(flatItems)); + this._visible$.next(this.createTree(visibleItems)); + return flatItems; + } + + add(items: T[]): T[] { + const map = new Map(); + items.forEach(item => map.set(item[this.id], item)); + + const flatItems = this.filterWith(map); + map.forEach(pushValueTo(flatItems)); + + flatItems.sort(this.sort); + const visibleItems = flatItems.filter(item => !this.hide(item)); + + return this.publish(flatItems, visibleItems); + } + + patch(identifier: string, props: Partial): T[] | false { + const flatItems = this._flat$.value; + const index = flatItems.findIndex(item => item[this.id] === identifier); + if (index < 0) return false; + + flatItems[index] = { ...flatItems[index], ...props }; + + flatItems.sort(this.sort); + const visibleItems = flatItems.filter(item => !this.hide(item)); + + return this.publish(flatItems, visibleItems); + } + + remove(identifiers: string[]): T[] { + const set = new Set(); + identifiers.forEach(id => set.add(id)); + + const flatItems = this.filterWith(set); + const visibleItems = flatItems.filter(item => !this.hide(item)); + + return this.publish(flatItems, visibleItems); + } + + search(params: Partial, tree = this.tree): TreeNode { + const searchKeys = Object.keys(params); + + return tree.reduce( + (acc, node) => + acc + ? acc + : searchKeys.every(key => node[key] === params[key]) + ? node + : node.children + ? this.search(params, node.children) + : acc, + null, + ); + } +} + +@Injectable({ + providedIn: 'root', +}) +export class RoutesService extends AbstractTreeService { + readonly id = 'name'; + readonly parentId = 'parentName'; + readonly hide = (item: ABP.Route) => item.invisible; + readonly sort = (a: ABP.Route, b: ABP.Route) => a.order - b.order; +} + +@Injectable({ + providedIn: 'root', +}) +export class SettingTabsService extends AbstractTreeService { + readonly id = 'name'; + readonly parentId = 'parentName'; + readonly hide = (setting: ABP.Tab) => setting.invisible || !this.isGranted(setting); + readonly sort = (a: ABP.Tab, b: ABP.Tab) => a.order - b.order; + + constructor(private store: Store) { + super(); + } + + private isGranted(setting: ABP.Tab): boolean { + return this.store.selectSnapshot(ConfigState.getGrantedPolicy(setting.requiredPolicy)); + } +} diff --git a/npm/ng-packs/packages/core/src/lib/states/config.state.ts b/npm/ng-packs/packages/core/src/lib/states/config.state.ts index 0618f127c5..d9b738b2aa 100644 --- a/npm/ng-packs/packages/core/src/lib/states/config.state.ts +++ b/npm/ng-packs/packages/core/src/lib/states/config.state.ts @@ -4,18 +4,11 @@ import { Action, createSelector, Selector, State, StateContext, Store } from '@n import { of, throwError } from 'rxjs'; import { catchError, switchMap, tap } from 'rxjs/operators'; import snq from 'snq'; -import { - AddRoute, - GetAppConfiguration, - PatchRouteByName, - SetEnvironment, -} from '../actions/config.actions'; +import { GetAppConfiguration, SetEnvironment } from '../actions/config.actions'; import { RestOccurError } from '../actions/rest.actions'; import { SetLanguage } from '../actions/session.actions'; import { ApplicationConfiguration } from '../models/application-configuration'; -import { ABP } from '../models/common'; import { Config } from '../models/config'; -import { organizeRoutes } from '../utils/route-utils'; import { SessionState } from './session.state'; @State({ @@ -64,23 +57,6 @@ export class ConfigState { return selector; } - static getRoute(path?: string, name?: string, url?: string) { - const selector = createSelector([ConfigState], (state: Config.State) => { - const { flattedRoutes } = state; - return (flattedRoutes as ABP.FullRoute[]).find(route => { - if (path && route.path === path) { - return route; - } else if (name && route.name === name) { - return route; - } else if (url && route.url === url) { - return route; - } - }); - }); - - return selector; - } - static getApiUrl(key?: string) { const selector = createSelector([ConfigState], (state: Config.State): string => { return (state.environment.apis[key || 'default'] || state.environment.apis.default).url; @@ -246,84 +222,6 @@ export class ConfigState { ); } - @Action(PatchRouteByName) - patchRoute( - { patchState, getState }: StateContext, - { name, newValue }: PatchRouteByName, - ) { - let routes: ABP.FullRoute[] = getState().routes; - - routes = patchRouteDeep(routes, name, newValue); - - const flattedRoutes = getState().flattedRoutes; - const index = flattedRoutes.findIndex(route => route.name === name); - - if (index > -1) { - flattedRoutes[index] = { ...flattedRoutes[index], ...newValue } as ABP.FullRoute; - } - - return patchState({ - routes, - flattedRoutes, - }); - } - - @Action(AddRoute) - addRoute({ patchState, getState }: StateContext, { payload }: AddRoute) { - let routes: ABP.FullRoute[] = getState().routes; - const flattedRoutes = getState().flattedRoutes; - const route: ABP.FullRoute = { ...payload }; - - if (route.parentName) { - const index = flattedRoutes.findIndex(r => r.name === route.parentName); - - if (index < 0) return; - - const parent = flattedRoutes[index]; - if ((parent.url || '').replace('/', '')) { - route.url = `${parent.url}/${route.path}`; - } else { - route.url = `/${route.path}`; - } - - route.children = route.children || []; - route.order = route.order || route.order === 0 ? route.order : (parent.children || []).length; - parent.children = [...(parent.children || []), route].sort((a, b) => a.order - b.order); - - flattedRoutes[index] = parent; - flattedRoutes.push(route); - - let parentName = parent.name; - const parentNameArr = [parentName]; - - while (parentName) { - parentName = snq(() => flattedRoutes.find(r => r.name === parentName).parentName); - - if (parentName) { - parentNameArr.unshift(parentName); - } - } - - routes = updateRouteDeep(routes, parentNameArr, parent); - } else { - route.url = `/${route.path}`; - - if (route.order || route.order === 0) { - routes = [...routes, route].sort((a, b) => a.order - b.order); - } else { - route.order = routes.length; - routes = [...routes, route]; - } - - flattedRoutes.push(route); - } - - return patchState({ - routes, - flattedRoutes, - }); - } - @Action(SetEnvironment) setEnvironment({ patchState }: StateContext, { environment }: SetEnvironment) { return patchState({ @@ -331,65 +229,3 @@ export class ConfigState { }); } } - -function patchRouteDeep( - routes: ABP.FullRoute[], - name: string, - newValue: Partial, - parentUrl: string = '', -): ABP.FullRoute[] { - routes = routes.map(route => { - if (route.name === name) { - newValue.url = `${parentUrl}/${(!newValue.path || newValue.path === '' - ? route.path - : newValue.path) || ''}`; - - if (newValue.children && newValue.children.length) { - newValue.children = newValue.children.map(child => ({ - ...child, - url: `${newValue.url}/${child.path}`.replace('//', '/'), - })); - } - - return { ...route, ...newValue }; - } else if (route.children && route.children.length) { - route.children = patchRouteDeep( - route.children, - name, - newValue, - (parentUrl || '/') + route.path, - ); - } - - return route; - }); - - if (parentUrl) { - // recursive block - return routes; - } - - return organizeRoutes(routes); -} - -function updateRouteDeep( - routes: ABP.FullRoute[], - parentNameArr: string[], - newValue: ABP.FullRoute, - parentIndex = 0, -) { - const index = routes.findIndex(route => route.name === parentNameArr[parentIndex]); - - if (parentIndex === parentNameArr.length - 1) { - routes[index] = newValue; - } else { - routes[index].children = updateRouteDeep( - routes[index].children, - parentNameArr, - newValue, - parentIndex + 1, - ); - } - - return routes; -} diff --git a/npm/ng-packs/packages/core/src/lib/tests/array-utils.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/array-utils.spec.ts new file mode 100644 index 0000000000..3ccc91461c --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tests/array-utils.spec.ts @@ -0,0 +1,17 @@ +import { pushValueTo } from '../utils/array-utils'; + +describe('Array Utils', () => { + describe('#pushValueTo', () => { + test.each` + source | target | expected + ${[]} | ${[0, 1, 2, 3]} | ${[0, 1, 2, 3]} + ${[3]} | ${[0, 1, 2]} | ${[0, 1, 2, 3]} + ${[2, 3]} | ${[0, 1]} | ${[0, 1, 2, 3]} + ${[1, 2, 3]} | ${[0]} | ${[0, 1, 2, 3]} + ${[0, 1, 2, 3]} | ${[]} | ${[0, 1, 2, 3]} + `('should push $source to $target when called in forEach', ({ source, target, expected }) => { + source.forEach(pushValueTo(target)); + expect(target).toEqual(expected); + }); + }); +}); diff --git a/npm/ng-packs/packages/core/src/lib/tests/config-state.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/config-state.service.spec.ts index 17de9efb9e..a34e3eaa4e 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/config-state.service.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/config-state.service.spec.ts @@ -29,43 +29,6 @@ const CONFIG_STATE_DATA = { requirements: { layouts: [null, null, null], }, - routes: [ - { - name: '::Menu:Home', - path: '', - children: [], - url: '/', - }, - { - name: 'AbpAccount::Menu:Account', - path: 'account', - invisible: true, - layout: 'application', - children: [ - { - path: 'login', - name: 'AbpAccount::Login', - order: 1, - url: '/account/login', - }, - ], - url: '/account', - }, - ], - flattedRoutes: [ - { - name: '::Menu:Home', - path: '', - children: [], - url: '/', - }, - { - name: '::Menu:Identity', - path: 'identity', - children: [], - url: '/identity', - }, - ], localization: { values: { MyProjectName: { diff --git a/npm/ng-packs/packages/core/src/lib/tests/config.plugin.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/config.plugin.spec.ts index a45ab25993..f098f44202 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/config.plugin.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/config.plugin.spec.ts @@ -1,351 +1,34 @@ -import { RouterTestingModule } from '@angular/router/testing'; -import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest'; -import { NgxsModule, Store } from '@ngxs/store'; -import { OAuthModule } from 'angular-oauth2-oidc'; -import { environment } from '../../../../../apps/dev-app/src/environments/environment'; -import { RouterOutletComponent } from '../components'; -import { CoreModule } from '../core.module'; -import { eLayoutType } from '../enums/common'; +import { InitState } from '@ngxs/store'; import { ABP } from '../models'; import { ConfigPlugin } from '../plugins'; -import { ConfigState } from '../states'; -import { addAbpRoutes } from '../utils'; -addAbpRoutes([ - { - name: 'AbpUiNavigation::Menu:Administration', - path: '', - order: 1, - wrapper: true, +const options: ABP.Root = { + environment: { + production: false, }, - { - name: 'AbpIdentity::Menu:IdentityManagement', - path: 'identity', - order: 1, - parentName: 'AbpUiNavigation::Menu:Administration', - layout: eLayoutType.application, - iconClass: 'fa fa-id-card-o', - children: [ - { path: 'roles', name: 'AbpIdentity::Roles', order: 2, requiredPolicy: 'AbpIdentity.Roles' }, - { path: 'users', name: 'AbpIdentity::Users', order: 1, requiredPolicy: 'AbpIdentity.Users' }, - ], - }, - { - name: 'AbpAccount::Menu:Account', - path: 'account', - invisible: true, - layout: eLayoutType.application, - children: [ - { path: 'login', name: 'AbpAccount::Login', order: 1 }, - { path: 'register', name: 'AbpAccount::Register', order: 2 }, - ], - }, - { - name: 'AbpTenantManagement::Menu:TenantManagement', - path: 'tenant-management', - parentName: 'AbpUiNavigation::Menu:Administration', - layout: eLayoutType.application, - iconClass: 'fa fa-users', - children: [ - { - path: 'tenants', - name: 'AbpTenantManagement::Tenants', - order: 1, - requiredPolicy: 'AbpTenantManagement.Tenants', - }, - ], - }, -]); - -const expectedState = { - environment, - routes: [ - { - name: '::Menu:Home', - path: '', - children: [], - url: '/', - order: 1, - }, - { - name: 'AbpUiNavigation::Menu:Administration', - path: '', - order: 1, - wrapper: true, - children: [ - { - name: 'AbpIdentity::Menu:IdentityManagement', - path: 'identity', - order: 1, - parentName: 'AbpUiNavigation::Menu:Administration', - layout: 'application', - iconClass: 'fa fa-id-card-o', - children: [ - { - path: 'users', - name: 'AbpIdentity::Users', - order: 1, - requiredPolicy: 'AbpIdentity.Users', - url: '/identity/users', - }, - { - path: 'roles', - name: 'AbpIdentity::Roles', - order: 2, - requiredPolicy: 'AbpIdentity.Roles', - url: '/identity/roles', - }, - ], - url: '/identity', - }, - { - name: 'AbpTenantManagement::Menu:TenantManagement', - path: 'tenant-management', - parentName: 'AbpUiNavigation::Menu:Administration', - layout: 'application', - iconClass: 'fa fa-users', - children: [ - { - path: 'tenants', - name: 'AbpTenantManagement::Tenants', - order: 1, - requiredPolicy: 'AbpTenantManagement.Tenants', - url: '/tenant-management/tenants', - }, - ], - url: '/tenant-management', - order: 2, - }, - ], - }, - { - name: 'AbpAccount::Menu:Account', - path: 'account', - invisible: true, - layout: 'application', - children: [ - { - path: 'login', - name: 'AbpAccount::Login', - order: 1, - url: '/account/login', - }, - { - path: 'register', - name: 'AbpAccount::Register', - order: 2, - url: '/account/register', - }, - ], - url: '/account', - order: 2, - }, - ], - flattedRoutes: [ - { - name: '::Menu:Home', - path: '', - children: [], - url: '/', - order: 1, - }, - { - name: 'AbpUiNavigation::Menu:Administration', - path: '', - order: 1, - wrapper: true, - children: [ - { - name: 'AbpIdentity::Menu:IdentityManagement', - path: 'identity', - order: 1, - parentName: 'AbpUiNavigation::Menu:Administration', - layout: 'application', - iconClass: 'fa fa-id-card-o', - children: [ - { - path: 'users', - name: 'AbpIdentity::Users', - order: 1, - parentName: 'AbpIdentity::Menu:IdentityManagement', - requiredPolicy: 'AbpIdentity.Users', - url: '/identity/users', - }, - { - path: 'roles', - name: 'AbpIdentity::Roles', - order: 2, - parentName: 'AbpIdentity::Menu:IdentityManagement', - requiredPolicy: 'AbpIdentity.Roles', - url: '/identity/roles', - }, - ], - url: '/identity', - }, - { - name: 'AbpTenantManagement::Menu:TenantManagement', - path: 'tenant-management', - parentName: 'AbpUiNavigation::Menu:Administration', - layout: 'application', - iconClass: 'fa fa-users', - children: [ - { - path: 'tenants', - name: 'AbpTenantManagement::Tenants', - order: 1, - parentName: 'AbpTenantManagement::Menu:TenantManagement', - requiredPolicy: 'AbpTenantManagement.Tenants', - url: '/tenant-management/tenants', - }, - ], - url: '/tenant-management', - order: 2, - }, - ], - }, - { - name: 'AbpIdentity::Menu:IdentityManagement', - path: 'identity', - order: 1, - parentName: 'AbpUiNavigation::Menu:Administration', - layout: 'application', - iconClass: 'fa fa-id-card-o', - children: [ - { - path: 'users', - name: 'AbpIdentity::Users', - order: 1, - parentName: 'AbpIdentity::Menu:IdentityManagement', - requiredPolicy: 'AbpIdentity.Users', - url: '/identity/users', - }, - { - path: 'roles', - name: 'AbpIdentity::Roles', - order: 2, - parentName: 'AbpIdentity::Menu:IdentityManagement', - requiredPolicy: 'AbpIdentity.Roles', - url: '/identity/roles', - }, - ], - url: '/identity', - }, - { - path: 'users', - name: 'AbpIdentity::Users', - order: 1, - parentName: 'AbpIdentity::Menu:IdentityManagement', - requiredPolicy: 'AbpIdentity.Users', - url: '/identity/users', - }, - { - path: 'roles', - name: 'AbpIdentity::Roles', - order: 2, - parentName: 'AbpIdentity::Menu:IdentityManagement', - requiredPolicy: 'AbpIdentity.Roles', - url: '/identity/roles', - }, - { - name: 'AbpTenantManagement::Menu:TenantManagement', - path: 'tenant-management', - parentName: 'AbpUiNavigation::Menu:Administration', - layout: 'application', - iconClass: 'fa fa-users', - children: [ - { - path: 'tenants', - name: 'AbpTenantManagement::Tenants', - order: 1, - parentName: 'AbpTenantManagement::Menu:TenantManagement', - requiredPolicy: 'AbpTenantManagement.Tenants', - url: '/tenant-management/tenants', - }, - ], - url: '/tenant-management', - order: 2, - }, - { - path: 'tenants', - name: 'AbpTenantManagement::Tenants', - order: 1, - parentName: 'AbpTenantManagement::Menu:TenantManagement', - requiredPolicy: 'AbpTenantManagement.Tenants', - url: '/tenant-management/tenants', - }, - { - name: 'AbpAccount::Menu:Account', - path: 'account', - invisible: true, - layout: 'application', - children: [ - { - path: 'login', - name: 'AbpAccount::Login', - order: 1, - parentName: 'AbpAccount::Menu:Account', - url: '/account/login', - }, - { - path: 'register', - name: 'AbpAccount::Register', - order: 2, - parentName: 'AbpAccount::Menu:Account', - url: '/account/register', - }, - ], - url: '/account', - order: 2, - }, - { - path: 'login', - name: 'AbpAccount::Login', - order: 1, - parentName: 'AbpAccount::Menu:Account', - url: '/account/login', - }, - { - path: 'register', - name: 'AbpAccount::Register', - order: 2, - parentName: 'AbpAccount::Menu:Account', - url: '/account/register', - }, - ], }; -describe('ConfigPlugin', () => { - let spectator: SpectatorService; - const createService = createServiceFactory({ - service: ConfigPlugin, - imports: [ - NgxsModule.forRoot([ConfigState]), - CoreModule.forRoot({ environment }), - OAuthModule.forRoot(), - RouterTestingModule.withRoutes([ - { - path: '', - component: RouterOutletComponent, - data: { - routes: { - name: '::Menu:Home', - } as ABP.Route, - }, - }, - { path: 'identity', component: RouterOutletComponent }, - { path: 'account', component: RouterOutletComponent }, - { path: 'tenant-management', component: RouterOutletComponent }, - ]), - ], - }); +const event = new InitState(); - beforeEach(() => { - spectator = createService(); - }); +const state = { + ConfigState: { + foo: 'bar', + ...options, + }, +}; +describe('ConfigPlugin', () => { it('should ConfigState must be create with correct datas', () => { - const store = spectator.get(Store); - const state = store.selectSnapshot(ConfigState); - expect(state).toEqual(expectedState); + const next = jest.fn(); + const plugin = new ConfigPlugin(options); + plugin.handle({ ConfigState: { foo: 'bar' } }, event, next); + expect(next).toHaveBeenCalledWith(state, event); + expect(next).toHaveBeenCalledTimes(1); + next.mockClear(); + + delete state.ConfigState.environment; + plugin.handle(state, event, next); + expect(next).toHaveBeenCalledWith(state, event); + expect(next).toHaveBeenCalledTimes(1); }); }); diff --git a/npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts index 189a9ab6d9..5966ba0728 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts @@ -1,13 +1,11 @@ +import { HttpClient } from '@angular/common/http'; import { createServiceFactory, SpectatorService, SpyObject } from '@ngneat/spectator/jest'; import { Store } from '@ngxs/store'; -import clone from 'just-clone'; import { of, ReplaySubject, timer } from 'rxjs'; -import { AddRoute, PatchRouteByName, SetLanguage } from '../actions'; -import { ABP } from '../models'; +import { SetLanguage } from '../actions'; import { Config } from '../models/config'; import { ApplicationConfigurationService, ConfigStateService } from '../services'; import { ConfigState } from '../states'; -import { HttpClient } from '@angular/common/http'; export const CONFIG_STATE_DATA = { environment: { @@ -33,61 +31,6 @@ export const CONFIG_STATE_DATA = { requirements: { layouts: [null, null, null], }, - routes: [ - { - name: '::Menu:Home', - path: '', - children: [], - url: '/', - }, - { - name: 'AbpAccount::Menu:Account', - path: 'account', - invisible: true, - layout: 'application', - children: [ - { - path: 'login', - name: 'AbpAccount::Login', - order: 1, - url: '/account/login', - parentName: 'AbpAccount::Menu:Account', - }, - ], - url: '/account', - }, - ], - flattedRoutes: [ - { - name: '::Menu:Home', - path: '', - children: [], - url: '/', - }, - { - name: 'AbpAccount::Menu:Account', - path: 'account', - invisible: true, - layout: 'application', - children: [ - { - path: 'login', - name: 'AbpAccount::Login', - order: 1, - url: '/account/login', - parentName: 'AbpAccount::Menu:Account', - }, - ], - url: '/account', - }, - { - path: 'login', - name: 'AbpAccount::Login', - order: 1, - url: '/account/login', - parentName: 'AbpAccount::Menu:Account', - }, - ], localization: { values: { MyProjectName: { @@ -208,17 +151,6 @@ describe('ConfigState', () => { }); }); - describe('#getRoute', () => { - it('should return route', () => { - expect(ConfigState.getRoute(null, '::Menu:Home')(CONFIG_STATE_DATA)).toEqual( - CONFIG_STATE_DATA.flattedRoutes[0], - ); - expect(ConfigState.getRoute('account')(CONFIG_STATE_DATA)).toEqual( - CONFIG_STATE_DATA.flattedRoutes[1], - ); - }); - }); - describe('#getApiUrl', () => { it('should return api url', () => { expect(ConfigState.getApiUrl('other')(CONFIG_STATE_DATA)).toEqual( @@ -330,121 +262,4 @@ describe('ConfigState', () => { }); }); }); - - describe('#PatchRouteByName', () => { - it('should patch the route', () => { - let patchStateArg; - - const patchState = jest.fn(s => (patchStateArg = s)); - const getState = jest.fn(() => clone(CONFIG_STATE_DATA)); - - state.patchRoute( - { patchState, getState } as any, - new PatchRouteByName('::Menu:Home', { - name: 'Home', - path: 'home', - children: [{ path: 'dashboard', name: 'Dashboard' }], - }), - ); - - expect(patchStateArg.routes[0]).toEqual({ - name: 'Home', - path: 'home', - url: '/home', - children: [{ path: 'dashboard', name: 'Dashboard', url: '/home/dashboard' }], - }); - expect(patchStateArg.flattedRoutes[0]).toEqual({ - name: 'Home', - path: 'home', - url: '/home', - children: [{ path: 'dashboard', name: 'Dashboard', url: '/home/dashboard' }], - }); - }); - - it('should patch the route without path', () => { - let patchStateArg; - - const patchState = jest.fn(s => (patchStateArg = s)); - const getState = jest.fn(() => clone(CONFIG_STATE_DATA)); - - state.patchRoute( - { patchState, getState } as any, - new PatchRouteByName('::Menu:Home', { - name: 'Main', - children: [{ path: 'dashboard', name: 'Dashboard' }], - }), - ); - - expect(patchStateArg.routes[0]).toEqual({ - name: 'Main', - path: '', - url: '/', - children: [{ path: 'dashboard', name: 'Dashboard', url: '/dashboard' }], - }); - - expect(patchStateArg.flattedRoutes[0]).toEqual({ - name: 'Main', - path: '', - url: '/', - children: [{ path: 'dashboard', name: 'Dashboard', url: '/dashboard' }], - }); - }); - }); - - describe('#AddRoute', () => { - const newRoute = { - name: 'My new page', - children: [], - iconClass: 'fa fa-dashboard', - path: 'page', - invisible: false, - order: 2, - requiredPolicy: 'MyProjectName::MyNewPage', - } as Omit; - - test('should add a new route', () => { - let patchStateArg; - - const patchState = jest.fn(s => (patchStateArg = s)); - const getState = jest.fn(() => clone(CONFIG_STATE_DATA)); - - state.addRoute({ patchState, getState } as any, new AddRoute(newRoute)); - - expect(patchStateArg.routes[CONFIG_STATE_DATA.routes.length]).toEqual({ - ...newRoute, - url: '/page', - }); - expect(patchStateArg.flattedRoutes[CONFIG_STATE_DATA.flattedRoutes.length]).toEqual( - patchStateArg.routes[CONFIG_STATE_DATA.routes.length], - ); - }); - - it('should add a new child route', () => { - let patchStateArg; - - const patchState = jest.fn(s => (patchStateArg = s)); - const getState = jest.fn(() => clone(CONFIG_STATE_DATA)); - - state.addRoute( - { patchState, getState } as any, - new AddRoute({ ...newRoute, parentName: 'AbpAccount::Login' }), - ); - - expect(patchStateArg.routes[1].children[0].children[0]).toEqual({ - ...newRoute, - parentName: 'AbpAccount::Login', - url: '/account/login/page', - }); - - expect(patchStateArg.flattedRoutes[CONFIG_STATE_DATA.flattedRoutes.length]).toEqual( - patchStateArg.routes[1].children[0].children[0], - ); - - expect( - patchStateArg.flattedRoutes[ - CONFIG_STATE_DATA.flattedRoutes.findIndex(route => route.name === 'AbpAccount::Login') - ], - ).toEqual(patchStateArg.routes[1].children[0]); - }); - }); }); diff --git a/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts index e1a678a19f..89a6a683b9 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts @@ -1,13 +1,13 @@ +import { HttpClient } from '@angular/common/http'; import { Component, NgModule } from '@angular/core'; import { ActivatedRoute, RouterModule } from '@angular/router'; import { createRoutingFactory, SpectatorRouting } from '@ngneat/spectator/jest'; import { NgxsModule, Store } from '@ngxs/store'; import { DynamicLayoutComponent, RouterOutletComponent } from '../components'; -import { eLayoutType } from '../enums'; +import { eLayoutType } from '../enums/common'; import { ABP } from '../models'; -import { ConfigState, ReplaceableComponentsState } from '../states'; -import { ApplicationConfigurationService } from '../services'; -import { HttpClient } from '@angular/common/http'; +import { ApplicationConfigurationService, RoutesService } from '../services'; +import { ReplaceableComponentsState } from '../states'; @Component({ selector: 'abp-layout-application', @@ -48,28 +48,36 @@ class DummyComponent { constructor(public route: ActivatedRoute) {} } -const storeData = { - ConfigState: { - routes: [ - { - path: '', - wrapper: true, - children: [ - { - path: 'parentWithLayout', - layout: eLayoutType.application, - children: [ - { path: 'childWithoutLayout' }, - { path: 'childWithLayout', layout: eLayoutType.account }, - ], - }, - ], - }, - { path: 'withData', layout: eLayoutType.application }, - , - ] as ABP.FullRoute[], - environment: { application: {} }, +const routes: ABP.Route[] = [ + { + path: '', + name: 'Root', + }, + { + path: '/parentWithLayout', + name: 'ParentWithLayout', + parentName: 'Root', + layout: eLayoutType.application, + }, + { + path: '/parentWithLayout/childWithoutLayout', + name: 'ChildWithoutLayout', + parentName: 'ParentWithLayout', + }, + { + path: '/parentWithLayout/childWithLayout', + name: 'ChildWithLayout', + parentName: 'ParentWithLayout', + layout: eLayoutType.account, + }, + { + path: '/withData', + name: 'WithData', + layout: eLayoutType.application, }, +]; + +const storeData = { ReplaceableComponentsState: { replaceableComponents: [ { @@ -94,11 +102,7 @@ describe('DynamicLayoutComponent', () => { stubsEnabled: false, declarations: [DummyComponent, DynamicLayoutComponent], mocks: [ApplicationConfigurationService, HttpClient], - imports: [ - RouterModule, - DummyLayoutModule, - NgxsModule.forRoot([ConfigState, ReplaceableComponentsState]), - ], + imports: [RouterModule, DummyLayoutModule, NgxsModule.forRoot([ReplaceableComponentsState])], routes: [ { path: '', component: RouterOutletComponent }, { @@ -150,6 +154,8 @@ describe('DynamicLayoutComponent', () => { beforeEach(async () => { spectator = createComponent(); store = spectator.get(Store); + const routesService = spectator.get(RoutesService); + routesService.add(routes); store.reset(storeData); }); diff --git a/npm/ng-packs/packages/core/src/lib/tests/factory-utils.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/factory-utils.spec.ts new file mode 100644 index 0000000000..06e8a8d044 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tests/factory-utils.spec.ts @@ -0,0 +1,38 @@ +import { Injector, ModuleWithProviders, NgModule } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import { LazyModuleFactory } from '../utils/factory-utils'; + +@NgModule() +class Module { + static forChild(): ModuleWithProviders { + return { + ngModule: Module, + providers: [{ provide: 'foo', useValue: 'bar' }], + }; + } +} + +describe('LazyModuleFactory', () => { + const factory = new LazyModuleFactory(Module.forChild()); + + describe('#moduleType', () => { + it('should return the ngModule property of given ModuleWithProviders', () => { + expect(factory.moduleType).toBe(Module); + }); + }); + + describe('#create', () => { + it('should return an instance of NgModuleRef_', () => { + TestBed.configureTestingModule({}); + + const injector = TestBed.inject(Injector); + const moduleRef = factory.create(injector); + + expect('componentFactoryResolver' in moduleRef).toBe(true); + expect('destroy' in moduleRef).toBe(true); + expect('injector' in moduleRef).toBe(true); + expect('instance' in moduleRef).toBe(true); + expect('onDestroy' in moduleRef).toBe(true); + }); + }); +}); diff --git a/npm/ng-packs/packages/core/src/lib/tests/route-utils.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/route-utils.spec.ts new file mode 100644 index 0000000000..41a761b84d --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tests/route-utils.spec.ts @@ -0,0 +1,60 @@ +import { Component } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import { createRoutingFactory, SpectatorRouting } from '@ngneat/spectator/jest'; +import { RouterOutletComponent } from '../components'; +import { getRoutePath } from '../utils/route-utils'; + +// tslint:disable-next-line +@Component({ template: '' }) +class DummyComponent {} + +describe('Route Utils', () => { + describe('#getRoutePath', () => { + let spectator: SpectatorRouting; + const createRouting = createRoutingFactory({ + component: RouterOutletComponent, + stubsEnabled: false, + declarations: [DummyComponent], + imports: [RouterModule], + routes: [ + { + path: '', + children: [ + { + path: 'foo', + children: [ + { + path: 'bar', + children: [ + { + path: 'baz', + component: DummyComponent, + }, + ], + }, + ], + }, + ], + }, + ], + }); + + beforeEach(async () => { + spectator = createRouting(); + }); + + test.each` + url | expected + ${''} | ${'/'} + ${'/'} | ${'/'} + ${'/foo'} | ${'/foo'} + ${'/foo/bar'} | ${'/foo/bar'} + ${'/foo/bar/baz'} | ${'/foo/bar/baz'} + ${'/foo?bar=baz'} | ${'/foo'} + ${'/foo#bar'} | ${'/foo'} + `('should return $expected when url is $url', async ({ url, expected }) => { + await spectator.router.navigateByUrl(url); + expect(getRoutePath(spectator.router)).toBe(expected); + }); + }); +}); diff --git a/npm/ng-packs/packages/core/src/lib/tests/routes.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/routes.service.spec.ts new file mode 100644 index 0000000000..2392a86183 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tests/routes.service.spec.ts @@ -0,0 +1,113 @@ +import { take } from 'rxjs/operators'; +import { RoutesService } from '../services'; + +const routes = [ + { path: '/foo', name: 'foo' }, + { path: '/foo/bar', name: 'bar', parentName: 'foo', invisible: true, order: 2 }, + { path: '/foo/bar/baz', name: 'baz', parentName: 'bar', order: 1 }, + { path: '/foo/x', name: 'x', parentName: 'foo', order: 1 }, +]; + +describe('Routes Service', () => { + describe('#add', () => { + it('should add given routes as flat$, tree$, and visible$', async () => { + const service = new RoutesService(); + service.add(routes); + + const flat = await service.flat$.pipe(take(1)).toPromise(); + const tree = await service.tree$.pipe(take(1)).toPromise(); + const visible = await service.visible$.pipe(take(1)).toPromise(); + + expect(flat.length).toBe(4); + expect(flat[0].name).toBe('foo'); + expect(flat[1].name).toBe('baz'); + expect(flat[2].name).toBe('x'); + expect(flat[3].name).toBe('bar'); + + expect(tree.length).toBe(1); + expect(tree[0].name).toBe('foo'); + expect(tree[0].children.length).toBe(2); + expect(tree[0].children[0].name).toBe('x'); + expect(tree[0].children[1].name).toBe('bar'); + expect(tree[0].children[1].children[0].name).toBe('baz'); + + expect(visible.length).toBe(1); + expect(visible[0].name).toBe('foo'); + expect(visible[0].children.length).toBe(1); + expect(visible[0].children[0].name).toBe('x'); + }); + }); + + describe('#remove', () => { + it('should remove routes based on given routeNames', () => { + const service = new RoutesService(); + service.add(routes); + service.remove(['bar']); + + const flat = service.flat; + const tree = service.tree; + const visible = service.visible; + + expect(flat.length).toBe(2); + expect(flat[0].name).toBe('foo'); + expect(flat[1].name).toBe('x'); + + expect(tree.length).toBe(1); + expect(tree[0].name).toBe('foo'); + expect(tree[0].children.length).toBe(1); + expect(tree[0].children[0].name).toBe('x'); + + expect(visible.length).toBe(1); + expect(visible[0].name).toBe('foo'); + expect(visible[0].children.length).toBe(1); + expect(visible[0].children[0].name).toBe('x'); + }); + }); + + describe('#patch', () => { + it('should patch propeties of routes based on given routeNames', () => { + const service = new RoutesService(); + service.add(routes); + service.patch('x', { invisible: true }); + + const flat = service.flat; + const tree = service.tree; + const visible = service.visible; + + expect(flat.length).toBe(4); + expect(flat[0].name).toBe('foo'); + expect(flat[1].name).toBe('baz'); + expect(flat[2].name).toBe('x'); + expect(flat[3].name).toBe('bar'); + + expect(tree.length).toBe(1); + expect(tree[0].name).toBe('foo'); + expect(tree[0].children.length).toBe(2); + expect(tree[0].children[0].name).toBe('x'); + expect(tree[0].children[1].name).toBe('bar'); + expect(tree[0].children[1].children[0].name).toBe('baz'); + + expect(visible.length).toBe(1); + expect(visible[0].name).toBe('foo'); + expect(visible[0].children.length).toBe(0); + }); + + it('should return false when route name is not found', () => { + const service = new RoutesService(); + service.add(routes); + const result = service.patch('A man has no name.', { invisible: true }); + expect(result).toBe(false); + }); + }); + + describe('#search', () => { + it('should return node found when route name is not found', () => { + const service = new RoutesService(); + service.add(routes); + const result = service.search({ invisible: true }); + expect(result.name).toBe('bar'); + expect(result.children.length).toBe(1); + expect(result.children[0].name).toBe('baz'); + }); + }); +}); diff --git a/npm/ng-packs/packages/core/src/lib/tests/tree-utils.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/tree-utils.spec.ts new file mode 100644 index 0000000000..6115114872 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tests/tree-utils.spec.ts @@ -0,0 +1,77 @@ +import { BaseTreeNode, createTreeFromList, TreeNode } from '../utils/tree-utils'; + +const LIST_1 = [ + { id: 1, pid: null }, + { id: 2, pid: 1 }, +]; +const LIST_2 = [ + { id: 1, pid: null }, + { id: 2, pid: 1 }, + { id: 3, pid: 1 }, +]; +const LIST_3 = [ + { id: 1, pid: null }, + { id: 2, pid: 1 }, + { id: 3, pid: 2 }, +]; +const TREE_1 = [ + { id: 1, pid: null, isLeaf: false, children: [{ id: 2, pid: 1, isLeaf: true, children: [] }] }, +]; +const TREE_2 = [ + { + id: 1, + pid: null, + isLeaf: false, + children: [ + { id: 2, pid: 1, isLeaf: true, children: [] }, + { id: 3, pid: 1, isLeaf: true, children: [] }, + ], + }, +]; +const TREE_3 = [ + { + id: 1, + pid: null, + isLeaf: false, + children: [ + { id: 2, pid: 1, isLeaf: false, children: [{ id: 3, pid: 2, isLeaf: true, children: [] }] }, + ], + }, +]; +describe('Tree Utils', () => { + describe('createTreeFromList', () => { + test.each` + list | expected + ${LIST_1} | ${TREE_1} + ${LIST_2} | ${TREE_2} + ${LIST_3} | ${TREE_3} + `('should return $expected when given $list', ({ list, expected }: TestCreateTreeFromList) => { + const tree = createTreeFromList( + list, + x => x.id, + x => x.pid, + x => BaseTreeNode.create(x), + ); + + expect(removeParents(tree)).toEqual(expected); + }); + }); +}); + +function removeParents(tree: TreeNode[]) { + return tree.map(v => { + const { parent, ...node } = v; + node.children = removeParents(node.children); + return node; + }); +} + +interface TestCreateTreeFromList { + list: Model[]; + expected: TreeNode[]; +} + +interface Model { + id: 1; + pid: null; +} diff --git a/npm/ng-packs/packages/core/src/lib/utils/array-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/array-utils.ts new file mode 100644 index 0000000000..d2d74105b3 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/utils/array-utils.ts @@ -0,0 +1,6 @@ +export function pushValueTo(array: T[]) { + return (element: T) => { + array.push(element); + return array; + }; +} diff --git a/npm/ng-packs/packages/core/src/lib/utils/factory-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/factory-utils.ts new file mode 100644 index 0000000000..bc6273fa82 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/utils/factory-utils.ts @@ -0,0 +1,31 @@ +import { + Compiler, + Injector, + ModuleWithProviders, + NgModuleFactory, + NgModuleRef, + StaticProvider, + Type, +} from '@angular/core'; + +export class LazyModuleFactory extends NgModuleFactory { + get moduleType(): Type { + return this.moduleWithProviders.ngModule; + } + + constructor(private moduleWithProviders: ModuleWithProviders) { + super(); + } + + create(parentInjector: Injector | null): NgModuleRef { + const injector = Injector.create({ + parent: parentInjector, + providers: this.moduleWithProviders.providers as StaticProvider[], + }); + + const compiler = injector.get(Compiler); + const factory = compiler.compileModuleSync(this.moduleType); + + return factory.create(injector); + } +} diff --git a/npm/ng-packs/packages/core/src/lib/utils/index.ts b/npm/ng-packs/packages/core/src/lib/utils/index.ts index 4dde8134ef..6f6136b195 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/index.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/index.ts @@ -1,4 +1,6 @@ +export * from './array-utils'; export * from './common-utils'; +export * from './factory-utils'; export * from './form-utils'; export * from './generator-utils'; export * from './initial-utils'; @@ -7,3 +9,4 @@ export * from './localization-utils'; export * from './number-utils'; export * from './route-utils'; export * from './rxjs-utils'; +export * from './tree-utils'; diff --git a/npm/ng-packs/packages/core/src/lib/utils/route-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/route-utils.ts index 51abe4218b..7ef062986a 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/route-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/route-utils.ts @@ -1,85 +1,8 @@ -import { ABP } from '../models/common'; +import { PRIMARY_OUTLET, Router, UrlSegmentGroup } from '@angular/router'; -export function organizeRoutes( - routes: ABP.FullRoute[], - wrappers: ABP.FullRoute[] = [], - parentNameArr = [] as ABP.FullRoute[], - parentName: string = null, -): ABP.FullRoute[] { - const filter = route => { - if (route.children && route.children.length) { - route.children = organizeRoutes(route.children, wrappers, parentNameArr, route.name); - } +export function getRoutePath(router: Router) { + const emptyGroup = { segments: [] } as UrlSegmentGroup; + const primaryGroup = router.parseUrl(router.url).root.children[PRIMARY_OUTLET]; - if (route.parentName && route.parentName !== parentName) { - parentNameArr.push(route); - return false; - } - - return true; - }; - - if (parentName) { - // recursive block - return routes.filter(filter); - } - - const filteredRoutes = routes.filter(filter); - - if (parentNameArr.length) { - return sortRoutes(setChildRoute([...filteredRoutes, ...wrappers], parentNameArr)); - } - - return filteredRoutes; -} - -export function setChildRoute( - routes: ABP.FullRoute[], - parentNameArr: ABP.FullRoute[], -): ABP.FullRoute[] { - return routes.map(route => { - if (route.children && route.children.length) { - route.children = setChildRoute(route.children, parentNameArr); - } - - const foundedChildren = parentNameArr.filter(parent => parent.parentName === route.name); - if (foundedChildren && foundedChildren.length) { - route.children = [...(route.children || []), ...foundedChildren]; - } - - return route; - }); -} - -export function sortRoutes(routes: ABP.FullRoute[] = []): ABP.FullRoute[] { - if (!routes.length) return []; - return routes - .map((route, index) => { - return { - ...route, - order: typeof route.order === 'undefined' ? index + 1 : route.order, - }; - }) - .sort((a, b) => a.order - b.order) - .map(route => { - if (route.children && route.children.length) { - route.children = sortRoutes(route.children); - } - - return route; - }); -} - -const ABP_ROUTES = [] as ABP.FullRoute[]; - -export function addAbpRoutes(routes: ABP.FullRoute | ABP.FullRoute[]): void { - if (!Array.isArray(routes)) { - routes = [routes]; - } - - ABP_ROUTES.push(...routes); -} - -export function getAbpRoutes(): ABP.FullRoute[] { - return ABP_ROUTES; + return '/' + (primaryGroup || emptyGroup).segments.map(({ path }) => path).join('/'); } diff --git a/npm/ng-packs/packages/core/src/lib/utils/tree-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/tree-utils.ts new file mode 100644 index 0000000000..d4c0ba0ab3 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/utils/tree-utils.ts @@ -0,0 +1,65 @@ +export class BaseTreeNode { + children: TreeNode[] = []; + isLeaf = true; + parent: TreeNode; + + constructor(props: T) { + Object.assign(this, props); + } + + static create(props: T) { + return new BaseTreeNode(props) as TreeNode; + } +} + +export function createTreeFromList( + list: T[], + keySelector: (item: T) => number | string | symbol, + parentKeySelector: typeof keySelector, + valueMapper: (item: T) => R, +) { + const map = createMapFromList(list, keySelector, valueMapper); + const tree: NodeValue[] = []; + + list.forEach(row => { + const id = keySelector(row); + const parentId = parentKeySelector(row); + const node = map.get(id); + + if (parentId) { + const parent = map.get(parentId); + if (!parent) return; + (parent as any).children.push(node); + (parent as any).isLeaf = false; + (node as any).parent = parent; + } else { + tree.push(node); + } + }); + + return tree; +} + +export function createMapFromList( + list: T[], + keySelector: (item: T) => number | string | symbol, + valueMapper: (item: T) => R, +) { + type Key = ReturnType; + type Value = NodeValue; + const map = new Map(); + list.forEach(row => map.set(keySelector(row), valueMapper(row))); + return map; +} + +export type TreeNode = { + [K in keyof T]: T[K]; +} & { + children: TreeNode[]; + isLeaf: boolean; + parent?: TreeNode; +}; + +type NodeValue any> = F extends undefined + ? TreeNode + : ReturnType; diff --git a/npm/ng-packs/packages/identity-config/README.md b/npm/ng-packs/packages/identity-config/README.md deleted file mode 100644 index 800ae0093c..0000000000 --- a/npm/ng-packs/packages/identity-config/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# @abp/ng.identity.config - -[docs.abp.io](https://docs.abp.io) diff --git a/npm/ng-packs/packages/identity-config/jest.config.js b/npm/ng-packs/packages/identity-config/jest.config.js deleted file mode 100644 index 27701730cb..0000000000 --- a/npm/ng-packs/packages/identity-config/jest.config.js +++ /dev/null @@ -1,6 +0,0 @@ -const jestConfig = require('../../jest.config'); - -module.exports = { - ...jestConfig, - name: 'identity-config', -}; diff --git a/npm/ng-packs/packages/identity-config/ng-package.json b/npm/ng-packs/packages/identity-config/ng-package.json deleted file mode 100644 index d72870cd39..0000000000 --- a/npm/ng-packs/packages/identity-config/ng-package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", - "dest": "../../dist/identity-config", - "lib": { - "entryFile": "src/public-api.ts" - } -} diff --git a/npm/ng-packs/packages/identity-config/package.json b/npm/ng-packs/packages/identity-config/package.json deleted file mode 100644 index 02f8935cb7..0000000000 --- a/npm/ng-packs/packages/identity-config/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "@abp/ng.identity.config", - "version": "2.9.0", - "homepage": "https://abp.io", - "repository": { - "type": "git", - "url": "https://github.com/abpframework/abp.git" - }, - "publishConfig": { - "access": "public" - } -} diff --git a/npm/ng-packs/packages/identity-config/src/lib/identity-config.module.ts b/npm/ng-packs/packages/identity-config/src/lib/identity-config.module.ts deleted file mode 100644 index a354cd6a46..0000000000 --- a/npm/ng-packs/packages/identity-config/src/lib/identity-config.module.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { noop } from '@abp/ng.core'; -import { APP_INITIALIZER, NgModule } from '@angular/core'; -import { IdentityConfigService } from './services/identity-config.service'; - -@NgModule({ - providers: [{ provide: APP_INITIALIZER, deps: [IdentityConfigService], useFactory: noop, multi: true }], -}) -export class IdentityConfigModule {} diff --git a/npm/ng-packs/packages/identity-config/src/lib/services/identity-config.service.ts b/npm/ng-packs/packages/identity-config/src/lib/services/identity-config.service.ts deleted file mode 100644 index c1fdbb102d..0000000000 --- a/npm/ng-packs/packages/identity-config/src/lib/services/identity-config.service.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { addAbpRoutes, eLayoutType } from '@abp/ng.core'; -import { Injectable } from '@angular/core'; -import { eIdentityRouteNames } from '@abp/ng.identity'; - -@Injectable({ - providedIn: 'root', -}) -export class IdentityConfigService { - constructor() { - addAbpRoutes([ - { - name: eIdentityRouteNames.Administration, - path: '', - order: 1, - wrapper: true, - iconClass: 'fa fa-wrench', - }, - { - name: eIdentityRouteNames.IdentityManagement, - path: 'identity', - order: 1, - parentName: eIdentityRouteNames.Administration, - layout: eLayoutType.application, - iconClass: 'fa fa-id-card-o', - children: [ - { - path: 'roles', - name: eIdentityRouteNames.Roles, - order: 1, - requiredPolicy: 'AbpIdentity.Roles', - }, - { - path: 'users', - name: eIdentityRouteNames.Users, - order: 2, - requiredPolicy: 'AbpIdentity.Users', - }, - ], - }, - ]); - } -} diff --git a/npm/ng-packs/packages/identity-config/src/public-api.ts b/npm/ng-packs/packages/identity-config/src/public-api.ts deleted file mode 100644 index e702653211..0000000000 --- a/npm/ng-packs/packages/identity-config/src/public-api.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './lib/services/identity-config.service'; -export * from './lib/identity-config.module'; diff --git a/npm/ng-packs/packages/identity-config/tsconfig.lib.json b/npm/ng-packs/packages/identity-config/tsconfig.lib.json deleted file mode 100644 index 4f48db2e46..0000000000 --- a/npm/ng-packs/packages/identity-config/tsconfig.lib.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": "../../tsconfig.prod.json", - "compilerOptions": { - "outDir": "../../out-tsc/lib", - "target": "es2015", - "declaration": true, - "inlineSources": true, - "types": [], - "lib": ["dom", "es2018"] - }, - "angularCompilerOptions": { - "skipTemplateCodegen": true, - "strictMetadataEmit": true, - "fullTemplateTypeCheck": true, - "strictInjectionParameters": true, - "enableResourceInlining": true - }, - "exclude": ["src/test.ts", "**/*.spec.ts"] -} diff --git a/npm/ng-packs/packages/identity-config/tsconfig.lib.prod.json b/npm/ng-packs/packages/identity-config/tsconfig.lib.prod.json deleted file mode 100644 index b1d501abbc..0000000000 --- a/npm/ng-packs/packages/identity-config/tsconfig.lib.prod.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "./tsconfig.lib.json", - "angularCompilerOptions": { - "enableIvy": false - } -} \ No newline at end of file diff --git a/npm/ng-packs/packages/identity-config/tsconfig.spec.json b/npm/ng-packs/packages/identity-config/tsconfig.spec.json deleted file mode 100644 index 02f312c262..0000000000 --- a/npm/ng-packs/packages/identity-config/tsconfig.spec.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../../tsconfig.prod.json", - "compilerOptions": { - "emitDecoratorMetadata": true, - "esModuleInterop": true, - "outDir": "../../dist/out-tsc", - "module": "commonjs", - "types": ["jest", "node"] - }, - "include": ["**/*.spec.ts", "**/*.d.ts"] -} diff --git a/npm/ng-packs/packages/identity-config/tslint.json b/npm/ng-packs/packages/identity-config/tslint.json deleted file mode 100644 index 8c3919ea62..0000000000 --- a/npm/ng-packs/packages/identity-config/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "abp", - "camelCase" - ], - "component-selector": [ - true, - "element", - "abp", - "kebab-case" - ] - } -} diff --git a/npm/ng-packs/packages/identity/config/ng-package.json b/npm/ng-packs/packages/identity/config/ng-package.json new file mode 100644 index 0000000000..48b66ee803 --- /dev/null +++ b/npm/ng-packs/packages/identity/config/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../dist/identity/config", + "lib": { + "entryFile": "src/public-api.ts" + } +} diff --git a/npm/ng-packs/packages/identity/config/src/enums/index.ts b/npm/ng-packs/packages/identity/config/src/enums/index.ts new file mode 100644 index 0000000000..3bda94b078 --- /dev/null +++ b/npm/ng-packs/packages/identity/config/src/enums/index.ts @@ -0,0 +1 @@ +export * from './route-names'; diff --git a/npm/ng-packs/packages/identity/src/lib/enums/route-names.ts b/npm/ng-packs/packages/identity/config/src/enums/route-names.ts similarity index 74% rename from npm/ng-packs/packages/identity/src/lib/enums/route-names.ts rename to npm/ng-packs/packages/identity/config/src/enums/route-names.ts index 15933a5b77..0452f32f7b 100644 --- a/npm/ng-packs/packages/identity/src/lib/enums/route-names.ts +++ b/npm/ng-packs/packages/identity/config/src/enums/route-names.ts @@ -1,5 +1,4 @@ export const enum eIdentityRouteNames { - Administration = 'AbpUiNavigation::Menu:Administration', IdentityManagement = 'AbpIdentity::Menu:IdentityManagement', Roles = 'AbpIdentity::Roles', Users = 'AbpIdentity::Users', diff --git a/npm/ng-packs/packages/identity/config/src/identity-config.module.ts b/npm/ng-packs/packages/identity/config/src/identity-config.module.ts new file mode 100644 index 0000000000..745578f69a --- /dev/null +++ b/npm/ng-packs/packages/identity/config/src/identity-config.module.ts @@ -0,0 +1,12 @@ +import { ModuleWithProviders, NgModule } from '@angular/core'; +import { IDENTITY_ROUTE_PROVIDERS } from './providers/route.provider'; + +@NgModule() +export class IdentityConfigModule { + static forRoot(): ModuleWithProviders { + return { + ngModule: IdentityConfigModule, + providers: [IDENTITY_ROUTE_PROVIDERS], + }; + } +} diff --git a/npm/ng-packs/packages/identity/config/src/providers/index.ts b/npm/ng-packs/packages/identity/config/src/providers/index.ts new file mode 100644 index 0000000000..fe08efba8c --- /dev/null +++ b/npm/ng-packs/packages/identity/config/src/providers/index.ts @@ -0,0 +1 @@ +export * from './route.provider'; diff --git a/npm/ng-packs/packages/identity/config/src/providers/route.provider.ts b/npm/ng-packs/packages/identity/config/src/providers/route.provider.ts new file mode 100644 index 0000000000..3f88c68464 --- /dev/null +++ b/npm/ng-packs/packages/identity/config/src/providers/route.provider.ts @@ -0,0 +1,37 @@ +import { eLayoutType, RoutesService } from '@abp/ng.core'; +import { eThemeSharedRouteNames } from '@abp/ng.theme.shared'; +import { APP_INITIALIZER } from '@angular/core'; +import { eIdentityRouteNames } from '../enums/route-names'; + +export const IDENTITY_ROUTE_PROVIDERS = [ + { provide: APP_INITIALIZER, useFactory: configureRoutes, deps: [RoutesService], multi: true }, +]; + +export function configureRoutes(routes: RoutesService) { + return () => { + routes.add([ + { + path: '/identity', + name: eIdentityRouteNames.IdentityManagement, + parentName: eThemeSharedRouteNames.Administration, + iconClass: 'fa fa-id-card-o', + layout: eLayoutType.application, + order: 1, + }, + { + path: '/identity/roles', + name: eIdentityRouteNames.Roles, + parentName: eIdentityRouteNames.IdentityManagement, + requiredPolicy: 'AbpIdentity.Roles', + order: 1, + }, + { + path: '/identity/users', + name: eIdentityRouteNames.Users, + parentName: eIdentityRouteNames.IdentityManagement, + requiredPolicy: 'AbpIdentity.Users', + order: 2, + }, + ]); + }; +} diff --git a/npm/ng-packs/packages/identity/config/src/public-api.ts b/npm/ng-packs/packages/identity/config/src/public-api.ts new file mode 100644 index 0000000000..1497fe943b --- /dev/null +++ b/npm/ng-packs/packages/identity/config/src/public-api.ts @@ -0,0 +1,3 @@ +export * from './enums'; +export * from './identity-config.module'; +export * from './providers'; diff --git a/npm/ng-packs/packages/identity/ng-package.json b/npm/ng-packs/packages/identity/ng-package.json index 450d7324a0..aa178ed0d5 100644 --- a/npm/ng-packs/packages/identity/ng-package.json +++ b/npm/ng-packs/packages/identity/ng-package.json @@ -4,9 +4,5 @@ "lib": { "entryFile": "src/public-api.ts" }, - "whitelistedNonPeerDependencies": [ - "@abp/ng.theme.shared", - "@abp/ng.permission-management", - "@abp/ng.identity.config" - ] + "whitelistedNonPeerDependencies": ["@abp/ng.theme.shared", "@abp/ng.permission-management"] } diff --git a/npm/ng-packs/packages/identity/package.json b/npm/ng-packs/packages/identity/package.json index 33951995b1..38ec9cd9d9 100644 --- a/npm/ng-packs/packages/identity/package.json +++ b/npm/ng-packs/packages/identity/package.json @@ -7,7 +7,6 @@ "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.identity.config": "~2.9.0", "@abp/ng.permission-management": "~2.9.0", "@abp/ng.theme.shared": "~2.9.0" }, diff --git a/npm/ng-packs/packages/identity/src/lib/enums/index.ts b/npm/ng-packs/packages/identity/src/lib/enums/index.ts index 5ac9b6e8fa..07635cbbc8 100644 --- a/npm/ng-packs/packages/identity/src/lib/enums/index.ts +++ b/npm/ng-packs/packages/identity/src/lib/enums/index.ts @@ -1,2 +1 @@ export * from './components'; -export * from './route-names'; diff --git a/npm/ng-packs/packages/identity/src/lib/identity.module.ts b/npm/ng-packs/packages/identity/src/lib/identity.module.ts index fef2e22a36..9aeadac6a2 100644 --- a/npm/ng-packs/packages/identity/src/lib/identity.module.ts +++ b/npm/ng-packs/packages/identity/src/lib/identity.module.ts @@ -1,14 +1,14 @@ -import { CoreModule } from '@abp/ng.core'; -import { NgModule, Provider } from '@angular/core'; +import { CoreModule, LazyModuleFactory } from '@abp/ng.core'; +import { PermissionManagementModule } from '@abp/ng.permission-management'; +import { ThemeSharedModule } from '@abp/ng.theme.shared'; +import { ModuleWithProviders, NgModule, NgModuleFactory } from '@angular/core'; +import { NgbDropdownModule, NgbTabsetModule } from '@ng-bootstrap/ng-bootstrap'; +import { NgxValidateCoreModule } from '@ngx-validate/core'; import { NgxsModule } from '@ngxs/store'; import { RolesComponent } from './components/roles/roles.component'; +import { UsersComponent } from './components/users/users.component'; import { IdentityRoutingModule } from './identity-routing.module'; import { IdentityState } from './states/identity.state'; -import { NgbTabsetModule, NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; -import { ThemeSharedModule } from '@abp/ng.theme.shared'; -import { UsersComponent } from './components/users/users.component'; -import { PermissionManagementModule } from '@abp/ng.permission-management'; -import { NgxValidateCoreModule } from '@ngx-validate/core'; @NgModule({ declarations: [RolesComponent, UsersComponent], @@ -24,4 +24,15 @@ import { NgxValidateCoreModule } from '@ngx-validate/core'; NgxValidateCoreModule, ], }) -export class IdentityModule {} +export class IdentityModule { + static forChild(): ModuleWithProviders { + return { + ngModule: IdentityModule, + providers: [], + }; + } + + static forLazy(): NgModuleFactory { + return new LazyModuleFactory(IdentityModule.forChild()); + } +} diff --git a/npm/ng-packs/packages/setting-management-config/README.md b/npm/ng-packs/packages/setting-management-config/README.md deleted file mode 100644 index 7bd9716004..0000000000 --- a/npm/ng-packs/packages/setting-management-config/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# @abp/ng.setting-management.config - -[docs.abp.io](https://docs.abp.io) diff --git a/npm/ng-packs/packages/setting-management-config/jest.config.js b/npm/ng-packs/packages/setting-management-config/jest.config.js deleted file mode 100644 index 77ce8a5dfa..0000000000 --- a/npm/ng-packs/packages/setting-management-config/jest.config.js +++ /dev/null @@ -1,6 +0,0 @@ -const jestConfig = require('../../jest.config'); - -module.exports = { - ...jestConfig, - name: 'setting-management-config', -}; diff --git a/npm/ng-packs/packages/setting-management-config/ng-package.json b/npm/ng-packs/packages/setting-management-config/ng-package.json deleted file mode 100644 index 95273dc0a7..0000000000 --- a/npm/ng-packs/packages/setting-management-config/ng-package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", - "dest": "../../dist/setting-management-config", - "lib": { - "entryFile": "src/public-api.ts" - } -} diff --git a/npm/ng-packs/packages/setting-management-config/package.json b/npm/ng-packs/packages/setting-management-config/package.json deleted file mode 100644 index b846a57e29..0000000000 --- a/npm/ng-packs/packages/setting-management-config/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "@abp/ng.setting-management.config", - "version": "2.9.0", - "homepage": "https://abp.io", - "repository": { - "type": "git", - "url": "https://github.com/abpframework/abp.git" - }, - "publishConfig": { - "access": "public" - } -} diff --git a/npm/ng-packs/packages/setting-management-config/src/lib/services/setting-management-config.service.ts b/npm/ng-packs/packages/setting-management-config/src/lib/services/setting-management-config.service.ts deleted file mode 100644 index a5b319ccb1..0000000000 --- a/npm/ng-packs/packages/setting-management-config/src/lib/services/setting-management-config.service.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Injectable, Injector } from '@angular/core'; -import { addAbpRoutes, eLayoutType, PatchRouteByName, ABP } from '@abp/ng.core'; -import { getSettingTabs } from '@abp/ng.theme.shared'; -import { Store } from '@ngxs/store'; -import { eSettingManagementRouteNames } from '@abp/ng.setting-management'; - -@Injectable({ - providedIn: 'root', -}) -export class SettingManagementConfigService { - get store(): Store { - return this.injector.get(Store); - } - - constructor(private injector: Injector) { - const route = { - name: eSettingManagementRouteNames.Settings, - path: 'setting-management', - parentName: 'AbpUiNavigation::Menu:Administration', - requiredPolicy: 'AbpAccount.SettingManagement', - layout: eLayoutType.application, - order: 6, - iconClass: 'fa fa-cog', - } as ABP.FullRoute; - - addAbpRoutes(route); - - setTimeout(() => { - const tabs = getSettingTabs(); - if (!tabs || !tabs.length) { - this.store.dispatch( - new PatchRouteByName('AbpSettingManagement::Settings', { ...route, invisible: true }), - ); - } - }); - } -} diff --git a/npm/ng-packs/packages/setting-management-config/src/lib/setting-management-config.module.ts b/npm/ng-packs/packages/setting-management-config/src/lib/setting-management-config.module.ts deleted file mode 100644 index c11e712fa4..0000000000 --- a/npm/ng-packs/packages/setting-management-config/src/lib/setting-management-config.module.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { NgModule, APP_INITIALIZER } from '@angular/core'; -import { SettingManagementConfigService } from './services/setting-management-config.service'; -import { noop } from '@abp/ng.core'; - -@NgModule({ - providers: [{ provide: APP_INITIALIZER, deps: [SettingManagementConfigService], useFactory: noop, multi: true }], -}) -export class SettingManagementConfigModule {} diff --git a/npm/ng-packs/packages/setting-management-config/src/public-api.ts b/npm/ng-packs/packages/setting-management-config/src/public-api.ts deleted file mode 100644 index 236ed82079..0000000000 --- a/npm/ng-packs/packages/setting-management-config/src/public-api.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './lib/setting-management-config.module'; diff --git a/npm/ng-packs/packages/setting-management-config/tsconfig.lib.json b/npm/ng-packs/packages/setting-management-config/tsconfig.lib.json deleted file mode 100644 index 4f48db2e46..0000000000 --- a/npm/ng-packs/packages/setting-management-config/tsconfig.lib.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": "../../tsconfig.prod.json", - "compilerOptions": { - "outDir": "../../out-tsc/lib", - "target": "es2015", - "declaration": true, - "inlineSources": true, - "types": [], - "lib": ["dom", "es2018"] - }, - "angularCompilerOptions": { - "skipTemplateCodegen": true, - "strictMetadataEmit": true, - "fullTemplateTypeCheck": true, - "strictInjectionParameters": true, - "enableResourceInlining": true - }, - "exclude": ["src/test.ts", "**/*.spec.ts"] -} diff --git a/npm/ng-packs/packages/setting-management-config/tsconfig.lib.prod.json b/npm/ng-packs/packages/setting-management-config/tsconfig.lib.prod.json deleted file mode 100644 index b1d501abbc..0000000000 --- a/npm/ng-packs/packages/setting-management-config/tsconfig.lib.prod.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "./tsconfig.lib.json", - "angularCompilerOptions": { - "enableIvy": false - } -} \ No newline at end of file diff --git a/npm/ng-packs/packages/setting-management-config/tsconfig.spec.json b/npm/ng-packs/packages/setting-management-config/tsconfig.spec.json deleted file mode 100644 index 02f312c262..0000000000 --- a/npm/ng-packs/packages/setting-management-config/tsconfig.spec.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../../tsconfig.prod.json", - "compilerOptions": { - "emitDecoratorMetadata": true, - "esModuleInterop": true, - "outDir": "../../dist/out-tsc", - "module": "commonjs", - "types": ["jest", "node"] - }, - "include": ["**/*.spec.ts", "**/*.d.ts"] -} diff --git a/npm/ng-packs/packages/setting-management-config/tslint.json b/npm/ng-packs/packages/setting-management-config/tslint.json deleted file mode 100644 index 8c3919ea62..0000000000 --- a/npm/ng-packs/packages/setting-management-config/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "abp", - "camelCase" - ], - "component-selector": [ - true, - "element", - "abp", - "kebab-case" - ] - } -} diff --git a/npm/ng-packs/packages/setting-management/config/ng-package.json b/npm/ng-packs/packages/setting-management/config/ng-package.json new file mode 100644 index 0000000000..b2e794e38a --- /dev/null +++ b/npm/ng-packs/packages/setting-management/config/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../dist/setting-management/config", + "lib": { + "entryFile": "src/public-api.ts" + } +} diff --git a/npm/ng-packs/packages/setting-management/config/src/enums/index.ts b/npm/ng-packs/packages/setting-management/config/src/enums/index.ts new file mode 100644 index 0000000000..3bda94b078 --- /dev/null +++ b/npm/ng-packs/packages/setting-management/config/src/enums/index.ts @@ -0,0 +1 @@ +export * from './route-names'; diff --git a/npm/ng-packs/packages/setting-management/config/src/enums/route-names.ts b/npm/ng-packs/packages/setting-management/config/src/enums/route-names.ts new file mode 100644 index 0000000000..86ad145f5f --- /dev/null +++ b/npm/ng-packs/packages/setting-management/config/src/enums/route-names.ts @@ -0,0 +1,3 @@ +export const enum eSettingManagementRouteNames { + Settings = 'AbpSettingManagement::Settings', +} diff --git a/npm/ng-packs/packages/setting-management/config/src/providers/index.ts b/npm/ng-packs/packages/setting-management/config/src/providers/index.ts new file mode 100644 index 0000000000..fe08efba8c --- /dev/null +++ b/npm/ng-packs/packages/setting-management/config/src/providers/index.ts @@ -0,0 +1 @@ +export * from './route.provider'; diff --git a/npm/ng-packs/packages/setting-management/config/src/providers/route.provider.ts b/npm/ng-packs/packages/setting-management/config/src/providers/route.provider.ts new file mode 100644 index 0000000000..3ed75d33b0 --- /dev/null +++ b/npm/ng-packs/packages/setting-management/config/src/providers/route.provider.ts @@ -0,0 +1,42 @@ +import { eLayoutType, RoutesService, SettingTabsService } from '@abp/ng.core'; +import { eThemeSharedRouteNames } from '@abp/ng.theme.shared'; +import { APP_INITIALIZER } from '@angular/core'; +import { debounceTime, map } from 'rxjs/operators'; +import { eSettingManagementRouteNames } from '../enums/route-names'; + +export const SETTING_MANAGEMENT_ROUTE_PROVIDERS = [ + { provide: APP_INITIALIZER, useFactory: configureRoutes, deps: [RoutesService], multi: true }, + { + provide: APP_INITIALIZER, + useFactory: hideRoutes, + deps: [RoutesService, SettingTabsService], + multi: true, + }, +]; + +export function configureRoutes(routes: RoutesService) { + return () => { + routes.add([ + { + name: eSettingManagementRouteNames.Settings, + path: '/setting-management', + parentName: eThemeSharedRouteNames.Administration, + requiredPolicy: 'AbpAccount.SettingManagement', + layout: eLayoutType.application, + order: 6, + iconClass: 'fa fa-cog', + }, + ]); + }; +} + +export function hideRoutes(routes: RoutesService, tabs: SettingTabsService) { + return () => { + tabs.visible$ + .pipe( + debounceTime(0), + map(nodes => !nodes.length), + ) + .subscribe(invisible => routes.patch('AbpSettingManagement::Settings', { invisible })); + }; +} diff --git a/npm/ng-packs/packages/setting-management/config/src/public-api.ts b/npm/ng-packs/packages/setting-management/config/src/public-api.ts new file mode 100644 index 0000000000..dae3d291c2 --- /dev/null +++ b/npm/ng-packs/packages/setting-management/config/src/public-api.ts @@ -0,0 +1,3 @@ +export * from './enums'; +export * from './providers'; +export * from './setting-management-config.module'; diff --git a/npm/ng-packs/packages/setting-management/config/src/setting-management-config.module.ts b/npm/ng-packs/packages/setting-management/config/src/setting-management-config.module.ts new file mode 100644 index 0000000000..139166d487 --- /dev/null +++ b/npm/ng-packs/packages/setting-management/config/src/setting-management-config.module.ts @@ -0,0 +1,12 @@ +import { ModuleWithProviders, NgModule } from '@angular/core'; +import { SETTING_MANAGEMENT_ROUTE_PROVIDERS } from './providers/route.provider'; + +@NgModule() +export class SettingManagementConfigModule { + static forRoot(): ModuleWithProviders { + return { + ngModule: SettingManagementConfigModule, + providers: [SETTING_MANAGEMENT_ROUTE_PROVIDERS], + }; + } +} diff --git a/npm/ng-packs/packages/setting-management/ng-package.json b/npm/ng-packs/packages/setting-management/ng-package.json index 7a08dda36b..e5ba9f97fc 100644 --- a/npm/ng-packs/packages/setting-management/ng-package.json +++ b/npm/ng-packs/packages/setting-management/ng-package.json @@ -4,5 +4,5 @@ "lib": { "entryFile": "src/public-api.ts" }, - "whitelistedNonPeerDependencies": ["@abp/ng.theme.shared", "@abp/ng.setting-management.config"] + "whitelistedNonPeerDependencies": ["@abp/ng.theme.shared"] } diff --git a/npm/ng-packs/packages/setting-management/package.json b/npm/ng-packs/packages/setting-management/package.json index 7d31672873..969510305e 100644 --- a/npm/ng-packs/packages/setting-management/package.json +++ b/npm/ng-packs/packages/setting-management/package.json @@ -7,7 +7,6 @@ "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.setting-management.config": "~2.9.0", "@abp/ng.theme.shared": "~2.9.0" }, "publishConfig": { diff --git a/npm/ng-packs/packages/setting-management/src/lib/actions/setting-management.actions.ts b/npm/ng-packs/packages/setting-management/src/lib/actions/setting-management.actions.ts index c3f082316c..9fd4c48238 100644 --- a/npm/ng-packs/packages/setting-management/src/lib/actions/setting-management.actions.ts +++ b/npm/ng-packs/packages/setting-management/src/lib/actions/setting-management.actions.ts @@ -1,6 +1,6 @@ -import { SettingTab } from '@abp/ng.theme.shared'; +import { ABP } from '@abp/ng.core'; export class SetSelectedSettingTab { static readonly type = '[SettingManagement] Set Selected Tab'; - constructor(public payload: SettingTab) {} + constructor(public payload: ABP.Tab) {} } diff --git a/npm/ng-packs/packages/setting-management/src/lib/components/setting-management.component.ts b/npm/ng-packs/packages/setting-management/src/lib/components/setting-management.component.ts index 7a7085e4e1..5b65c1f76d 100644 --- a/npm/ng-packs/packages/setting-management/src/lib/components/setting-management.component.ts +++ b/npm/ng-packs/packages/setting-management/src/lib/components/setting-management.component.ts @@ -1,7 +1,7 @@ -import { ConfigState } from '@abp/ng.core'; -import { getSettingTabs, SettingTab } from '@abp/ng.theme.shared'; -import { Component, OnInit, TrackByFunction } from '@angular/core'; +import { ABP, SettingTabsService } from '@abp/ng.core'; +import { Component, OnDestroy, OnInit, TrackByFunction } from '@angular/core'; import { Store } from '@ngxs/store'; +import { Subscription } from 'rxjs'; import { SetSelectedSettingTab } from '../actions/setting-management.actions'; import { SettingManagementState } from '../states/setting-management.state'; @@ -9,35 +9,34 @@ import { SettingManagementState } from '../states/setting-management.state'; selector: 'abp-setting-management', templateUrl: './setting-management.component.html', }) -export class SettingManagementComponent implements OnInit { - settings: SettingTab[] = []; +export class SettingManagementComponent implements OnDestroy, OnInit { + private subscription = new Subscription(); + settings: ABP.Tab[] = []; - set selected(value: SettingTab) { + set selected(value: ABP.Tab) { this.store.dispatch(new SetSelectedSettingTab(value)); } - get selected(): SettingTab { + get selected(): ABP.Tab { const value = this.store.selectSnapshot(SettingManagementState.getSelectedTab); - if ((!value || !value.component) && this.settings.length) { - return this.settings[0]; - } - - return value; + return value?.component ? value : this.settings[0] || ({} as ABP.Tab); } - trackByFn: TrackByFunction = (_, item) => item.name; + trackByFn: TrackByFunction = (_, item) => item.name; + + constructor(private store: Store, private settingTabs: SettingTabsService) {} - constructor(private store: Store) {} + ngOnDestroy() { + this.subscription.unsubscribe(); + } ngOnInit() { - this.settings = getSettingTabs() - .filter(setting => - this.store.selectSnapshot(ConfigState.getGrantedPolicy(setting.requiredPolicy)), - ) - .sort((a, b) => a.order - b.order); - - if (!this.selected && this.settings.length) { - this.selected = this.settings[0]; - } + this.subscription.add( + this.settingTabs.visible$.subscribe(settings => { + this.settings = settings; + + if (!this.selected) this.selected = this.settings[0]; + }), + ); } } diff --git a/npm/ng-packs/packages/setting-management/src/lib/models/setting-management.ts b/npm/ng-packs/packages/setting-management/src/lib/models/setting-management.ts index b0aedd4373..cb6f542cbc 100644 --- a/npm/ng-packs/packages/setting-management/src/lib/models/setting-management.ts +++ b/npm/ng-packs/packages/setting-management/src/lib/models/setting-management.ts @@ -1,7 +1,7 @@ -import { SettingTab } from '@abp/ng.theme.shared'; +import { ABP } from '@abp/ng.core'; export namespace SettingManagement { export interface State { - selectedTab: SettingTab; + selectedTab?: ABP.Tab; } } diff --git a/npm/ng-packs/packages/setting-management/src/lib/setting-management.module.ts b/npm/ng-packs/packages/setting-management/src/lib/setting-management.module.ts index 091a648524..fe2c0fdacc 100644 --- a/npm/ng-packs/packages/setting-management/src/lib/setting-management.module.ts +++ b/npm/ng-packs/packages/setting-management/src/lib/setting-management.module.ts @@ -1,9 +1,9 @@ -import { CoreModule } from '@abp/ng.core'; +import { CoreModule, LazyModuleFactory } from '@abp/ng.core'; import { ThemeSharedModule } from '@abp/ng.theme.shared'; -import { NgModule } from '@angular/core'; -import { SettingManagementRoutingModule } from './setting-management-routing.module'; -import { SettingManagementComponent } from './components/setting-management.component'; +import { ModuleWithProviders, NgModule, NgModuleFactory } from '@angular/core'; import { NgxsModule } from '@ngxs/store'; +import { SettingManagementComponent } from './components/setting-management.component'; +import { SettingManagementRoutingModule } from './setting-management-routing.module'; import { SettingManagementState } from './states/setting-management.state'; @NgModule({ @@ -16,4 +16,15 @@ import { SettingManagementState } from './states/setting-management.state'; NgxsModule.forFeature([SettingManagementState]), ], }) -export class SettingManagementModule {} +export class SettingManagementModule { + static forChild(): ModuleWithProviders { + return { + ngModule: SettingManagementModule, + providers: [], + }; + } + + static forLazy(): NgModuleFactory { + return new LazyModuleFactory(SettingManagementModule.forChild()); + } +} diff --git a/npm/ng-packs/packages/setting-management/src/lib/states/setting-management.state.ts b/npm/ng-packs/packages/setting-management/src/lib/states/setting-management.state.ts index 7490117b38..19a6ba8835 100644 --- a/npm/ng-packs/packages/setting-management/src/lib/states/setting-management.state.ts +++ b/npm/ng-packs/packages/setting-management/src/lib/states/setting-management.state.ts @@ -1,11 +1,11 @@ +import { Injectable } from '@angular/core'; import { Action, Selector, State, StateContext } from '@ngxs/store'; import { SetSelectedSettingTab } from '../actions/setting-management.actions'; import { SettingManagement } from '../models/setting-management'; -import { Injectable } from '@angular/core'; @State({ name: 'SettingManagementState', - defaults: { selectedTab: {} } as SettingManagement.State, + defaults: {}, }) @Injectable() export class SettingManagementState { diff --git a/npm/ng-packs/packages/tenant-management-config/README.md b/npm/ng-packs/packages/tenant-management-config/README.md deleted file mode 100644 index e2d3c3638b..0000000000 --- a/npm/ng-packs/packages/tenant-management-config/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# @abp/ng.tenant-management.config - -[docs.abp.io](https://docs.abp.io) diff --git a/npm/ng-packs/packages/tenant-management-config/jest.config.js b/npm/ng-packs/packages/tenant-management-config/jest.config.js deleted file mode 100644 index e146e10d0a..0000000000 --- a/npm/ng-packs/packages/tenant-management-config/jest.config.js +++ /dev/null @@ -1,6 +0,0 @@ -const jestConfig = require('../../jest.config'); - -module.exports = { - ...jestConfig, - name: 'tenant-management-config', -}; diff --git a/npm/ng-packs/packages/tenant-management-config/ng-package.json b/npm/ng-packs/packages/tenant-management-config/ng-package.json deleted file mode 100644 index d061bd7c44..0000000000 --- a/npm/ng-packs/packages/tenant-management-config/ng-package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", - "dest": "../../dist/tenant-management-config", - "lib": { - "entryFile": "src/public-api.ts" - } -} diff --git a/npm/ng-packs/packages/tenant-management-config/package.json b/npm/ng-packs/packages/tenant-management-config/package.json deleted file mode 100644 index 1434394e8b..0000000000 --- a/npm/ng-packs/packages/tenant-management-config/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "@abp/ng.tenant-management.config", - "version": "2.9.0", - "homepage": "https://abp.io", - "repository": { - "type": "git", - "url": "https://github.com/abpframework/abp.git" - }, - "publishConfig": { - "access": "public" - } -} diff --git a/npm/ng-packs/packages/tenant-management-config/src/lib/services/tenant-management-config.service.ts b/npm/ng-packs/packages/tenant-management-config/src/lib/services/tenant-management-config.service.ts deleted file mode 100644 index ab5ca4a081..0000000000 --- a/npm/ng-packs/packages/tenant-management-config/src/lib/services/tenant-management-config.service.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Injectable } from '@angular/core'; -import { addAbpRoutes, eLayoutType } from '@abp/ng.core'; -import { eTenantManagementRouteNames } from '@abp/ng.tenant-management'; -@Injectable({ - providedIn: 'root', -}) -export class TenantManagementConfigService { - constructor() { - addAbpRoutes([ - { - name: eTenantManagementRouteNames.Administration, - path: '', - order: 1, - wrapper: true, - iconClass: 'fa fa-wrench', - }, - { - name: eTenantManagementRouteNames.TenantManagement, - path: 'tenant-management', - parentName: eTenantManagementRouteNames.Administration, - layout: eLayoutType.application, - iconClass: 'fa fa-users', - children: [ - { - path: 'tenants', - name: eTenantManagementRouteNames.Tenants, - order: 1, - requiredPolicy: 'AbpTenantManagement.Tenants', - }, - ], - }, - ]); - } -} diff --git a/npm/ng-packs/packages/tenant-management-config/src/lib/tenant-management-config.module.ts b/npm/ng-packs/packages/tenant-management-config/src/lib/tenant-management-config.module.ts deleted file mode 100644 index 7f59d28be4..0000000000 --- a/npm/ng-packs/packages/tenant-management-config/src/lib/tenant-management-config.module.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { NgModule, APP_INITIALIZER } from '@angular/core'; -import { TenantManagementConfigService } from './services/tenant-management-config.service'; -import { noop } from '@abp/ng.core'; - -@NgModule({ - providers: [{ provide: APP_INITIALIZER, deps: [TenantManagementConfigService], useFactory: noop, multi: true }], -}) -export class TenantManagementConfigModule {} diff --git a/npm/ng-packs/packages/tenant-management-config/src/public-api.ts b/npm/ng-packs/packages/tenant-management-config/src/public-api.ts deleted file mode 100644 index a6d21694db..0000000000 --- a/npm/ng-packs/packages/tenant-management-config/src/public-api.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './lib/services/tenant-management-config.service'; -export * from './lib/tenant-management-config.module'; diff --git a/npm/ng-packs/packages/tenant-management-config/tsconfig.lib.json b/npm/ng-packs/packages/tenant-management-config/tsconfig.lib.json deleted file mode 100644 index 4f48db2e46..0000000000 --- a/npm/ng-packs/packages/tenant-management-config/tsconfig.lib.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": "../../tsconfig.prod.json", - "compilerOptions": { - "outDir": "../../out-tsc/lib", - "target": "es2015", - "declaration": true, - "inlineSources": true, - "types": [], - "lib": ["dom", "es2018"] - }, - "angularCompilerOptions": { - "skipTemplateCodegen": true, - "strictMetadataEmit": true, - "fullTemplateTypeCheck": true, - "strictInjectionParameters": true, - "enableResourceInlining": true - }, - "exclude": ["src/test.ts", "**/*.spec.ts"] -} diff --git a/npm/ng-packs/packages/tenant-management-config/tsconfig.lib.prod.json b/npm/ng-packs/packages/tenant-management-config/tsconfig.lib.prod.json deleted file mode 100644 index b1d501abbc..0000000000 --- a/npm/ng-packs/packages/tenant-management-config/tsconfig.lib.prod.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "./tsconfig.lib.json", - "angularCompilerOptions": { - "enableIvy": false - } -} \ No newline at end of file diff --git a/npm/ng-packs/packages/tenant-management-config/tsconfig.spec.json b/npm/ng-packs/packages/tenant-management-config/tsconfig.spec.json deleted file mode 100644 index 02f312c262..0000000000 --- a/npm/ng-packs/packages/tenant-management-config/tsconfig.spec.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../../tsconfig.prod.json", - "compilerOptions": { - "emitDecoratorMetadata": true, - "esModuleInterop": true, - "outDir": "../../dist/out-tsc", - "module": "commonjs", - "types": ["jest", "node"] - }, - "include": ["**/*.spec.ts", "**/*.d.ts"] -} diff --git a/npm/ng-packs/packages/tenant-management-config/tslint.json b/npm/ng-packs/packages/tenant-management-config/tslint.json deleted file mode 100644 index 8c3919ea62..0000000000 --- a/npm/ng-packs/packages/tenant-management-config/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "abp", - "camelCase" - ], - "component-selector": [ - true, - "element", - "abp", - "kebab-case" - ] - } -} diff --git a/npm/ng-packs/packages/tenant-management/config/ng-package.json b/npm/ng-packs/packages/tenant-management/config/ng-package.json new file mode 100644 index 0000000000..50f063edb5 --- /dev/null +++ b/npm/ng-packs/packages/tenant-management/config/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../dist/tenant-management/config", + "lib": { + "entryFile": "src/public-api.ts" + } +} diff --git a/npm/ng-packs/packages/tenant-management/config/src/enums/index.ts b/npm/ng-packs/packages/tenant-management/config/src/enums/index.ts new file mode 100644 index 0000000000..3bda94b078 --- /dev/null +++ b/npm/ng-packs/packages/tenant-management/config/src/enums/index.ts @@ -0,0 +1 @@ +export * from './route-names'; diff --git a/npm/ng-packs/packages/tenant-management/src/lib/enums/route-names.ts b/npm/ng-packs/packages/tenant-management/config/src/enums/route-names.ts similarity index 73% rename from npm/ng-packs/packages/tenant-management/src/lib/enums/route-names.ts rename to npm/ng-packs/packages/tenant-management/config/src/enums/route-names.ts index 8125e28a4f..267ddc2f08 100644 --- a/npm/ng-packs/packages/tenant-management/src/lib/enums/route-names.ts +++ b/npm/ng-packs/packages/tenant-management/config/src/enums/route-names.ts @@ -1,5 +1,4 @@ export const enum eTenantManagementRouteNames { - Administration = 'AbpUiNavigation::Menu:Administration', TenantManagement = 'AbpTenantManagement::Menu:TenantManagement', Tenants = 'AbpTenantManagement::Tenants', } diff --git a/npm/ng-packs/packages/tenant-management/config/src/providers/index.ts b/npm/ng-packs/packages/tenant-management/config/src/providers/index.ts new file mode 100644 index 0000000000..fe08efba8c --- /dev/null +++ b/npm/ng-packs/packages/tenant-management/config/src/providers/index.ts @@ -0,0 +1 @@ +export * from './route.provider'; diff --git a/npm/ng-packs/packages/tenant-management/config/src/providers/route.provider.ts b/npm/ng-packs/packages/tenant-management/config/src/providers/route.provider.ts new file mode 100644 index 0000000000..0be264745d --- /dev/null +++ b/npm/ng-packs/packages/tenant-management/config/src/providers/route.provider.ts @@ -0,0 +1,30 @@ +import { eLayoutType, RoutesService } from '@abp/ng.core'; +import { eThemeSharedRouteNames } from '@abp/ng.theme.shared'; +import { APP_INITIALIZER } from '@angular/core'; +import { eTenantManagementRouteNames } from '../enums/route-names'; + +export const TENANT_MANAGEMENT_ROUTE_PROVIDERS = [ + { provide: APP_INITIALIZER, useFactory: configureRoutes, deps: [RoutesService], multi: true }, +]; + +export function configureRoutes(routes: RoutesService) { + return () => { + routes.add([ + { + path: '/tenant-management', + name: eTenantManagementRouteNames.TenantManagement, + parentName: eThemeSharedRouteNames.Administration, + layout: eLayoutType.application, + iconClass: 'fa fa-users', + order: 2, + }, + { + path: '/tenant-management/tenants', + name: eTenantManagementRouteNames.Tenants, + parentName: eTenantManagementRouteNames.TenantManagement, + requiredPolicy: 'AbpTenantManagement.Tenants', + order: 1, + }, + ]); + }; +} diff --git a/npm/ng-packs/packages/tenant-management/config/src/public-api.ts b/npm/ng-packs/packages/tenant-management/config/src/public-api.ts new file mode 100644 index 0000000000..6c2c00a53a --- /dev/null +++ b/npm/ng-packs/packages/tenant-management/config/src/public-api.ts @@ -0,0 +1,3 @@ +export * from './enums'; +export * from './providers'; +export * from './tenant-management-config.module'; diff --git a/npm/ng-packs/packages/tenant-management/config/src/tenant-management-config.module.ts b/npm/ng-packs/packages/tenant-management/config/src/tenant-management-config.module.ts new file mode 100644 index 0000000000..2929f766c0 --- /dev/null +++ b/npm/ng-packs/packages/tenant-management/config/src/tenant-management-config.module.ts @@ -0,0 +1,12 @@ +import { ModuleWithProviders, NgModule } from '@angular/core'; +import { TENANT_MANAGEMENT_ROUTE_PROVIDERS } from './providers/route.provider'; + +@NgModule() +export class TenantManagementConfigModule { + static forRoot(): ModuleWithProviders { + return { + ngModule: TenantManagementConfigModule, + providers: [TENANT_MANAGEMENT_ROUTE_PROVIDERS], + }; + } +} diff --git a/npm/ng-packs/packages/tenant-management/ng-package.json b/npm/ng-packs/packages/tenant-management/ng-package.json index 959eae44ab..07fbab773b 100644 --- a/npm/ng-packs/packages/tenant-management/ng-package.json +++ b/npm/ng-packs/packages/tenant-management/ng-package.json @@ -4,9 +4,5 @@ "lib": { "entryFile": "src/public-api.ts" }, - "whitelistedNonPeerDependencies": [ - "@abp/ng.theme.shared", - "@abp/ng.feature-management", - "@abp/ng.tenant-management.config" - ] + "whitelistedNonPeerDependencies": ["@abp/ng.theme.shared", "@abp/ng.feature-management"] } diff --git a/npm/ng-packs/packages/tenant-management/package.json b/npm/ng-packs/packages/tenant-management/package.json index 18756a40cb..0921e0dd9e 100644 --- a/npm/ng-packs/packages/tenant-management/package.json +++ b/npm/ng-packs/packages/tenant-management/package.json @@ -8,7 +8,6 @@ }, "dependencies": { "@abp/ng.feature-management": "~2.9.0", - "@abp/ng.tenant-management.config": "~2.9.0", "@abp/ng.theme.shared": "~2.9.0" }, "publishConfig": { diff --git a/npm/ng-packs/packages/tenant-management/src/lib/enums/index.ts b/npm/ng-packs/packages/tenant-management/src/lib/enums/index.ts index 5ac9b6e8fa..07635cbbc8 100644 --- a/npm/ng-packs/packages/tenant-management/src/lib/enums/index.ts +++ b/npm/ng-packs/packages/tenant-management/src/lib/enums/index.ts @@ -1,2 +1 @@ export * from './components'; -export * from './route-names'; diff --git a/npm/ng-packs/packages/tenant-management/src/lib/tenant-management.module.ts b/npm/ng-packs/packages/tenant-management/src/lib/tenant-management.module.ts index 046a2b9842..035b658363 100644 --- a/npm/ng-packs/packages/tenant-management/src/lib/tenant-management.module.ts +++ b/npm/ng-packs/packages/tenant-management/src/lib/tenant-management.module.ts @@ -1,13 +1,13 @@ -import { CoreModule } from '@abp/ng.core'; +import { CoreModule, LazyModuleFactory } from '@abp/ng.core'; +import { FeatureManagementModule } from '@abp/ng.feature-management'; import { ThemeSharedModule } from '@abp/ng.theme.shared'; -import { NgModule, Provider } from '@angular/core'; +import { ModuleWithProviders, NgModule, NgModuleFactory } from '@angular/core'; import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; +import { NgxValidateCoreModule } from '@ngx-validate/core'; import { NgxsModule } from '@ngxs/store'; import { TenantsComponent } from './components/tenants/tenants.component'; import { TenantManagementState } from './states/tenant-management.state'; import { TenantManagementRoutingModule } from './tenant-management-routing.module'; -import { FeatureManagementModule } from '@abp/ng.feature-management'; -import { NgxValidateCoreModule } from '@ngx-validate/core'; @NgModule({ declarations: [TenantsComponent], @@ -22,4 +22,15 @@ import { NgxValidateCoreModule } from '@ngx-validate/core'; FeatureManagementModule, ], }) -export class TenantManagementModule {} +export class TenantManagementModule { + static forChild(): ModuleWithProviders { + return { + ngModule: TenantManagementModule, + providers: [], + }; + } + + static forLazy(): NgModuleFactory { + return new LazyModuleFactory(TenantManagementModule.forChild()); + } +} diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.ts b/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.ts index 9c90292d96..d3192057bb 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.ts @@ -1,7 +1,6 @@ import { eLayoutType, takeUntilDestroy } from '@abp/ng.core'; import { collapseWithMargin, slideFromBottom } from '@abp/ng.theme.shared'; import { AfterViewInit, Component, OnDestroy } from '@angular/core'; -import { Store } from '@ngxs/store'; import { fromEvent } from 'rxjs'; import { debounceTime } from 'rxjs/operators'; import { eThemeBasicComponents } from '../../enums/components'; @@ -25,8 +24,6 @@ export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy { navItemsComponentKey = eThemeBasicComponents.NavItems; - constructor(private store: Store) {} - private checkWindowWidth() { setTimeout(() => { if (window.innerWidth < 992) { diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.ts b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.ts index 7bae923989..6819807cd7 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.ts @@ -1,31 +1,31 @@ -import { - Component, - AfterViewInit, - TrackByFunction, - TemplateRef, - ViewChild, - OnDestroy, - Input, -} from '@angular/core'; import { ABP, - takeUntilDestroy, - SetLanguage, + ApplicationConfiguration, AuthService, ConfigState, - ApplicationConfiguration, SessionState, + SetLanguage, + takeUntilDestroy, } from '@abp/ng.core'; -import { LayoutState } from '../../states/layout.state'; -import { Store, Select } from '@ngxs/store'; -import { eNavigationElementNames } from '../../enums/navigation-element-names'; -import { AddNavigationElement } from '../../actions/layout.actions'; -import { map, filter } from 'rxjs/operators'; -import { Observable } from 'rxjs'; -import { Layout } from '../../models/layout'; +import { + AfterViewInit, + Component, + Input, + OnDestroy, + TemplateRef, + TrackByFunction, + ViewChild, +} from '@angular/core'; import { Navigate, RouterState } from '@ngxs/router-plugin'; -import snq from 'snq'; +import { Select, Store } from '@ngxs/store'; import compare from 'just-compare'; +import { Observable } from 'rxjs'; +import { filter, map } from 'rxjs/operators'; +import snq from 'snq'; +import { AddNavigationElement } from '../../actions/layout.actions'; +import { eNavigationElementNames } from '../../enums/navigation-element-names'; +import { Layout } from '../../models/layout'; +import { LayoutState } from '../../states/layout.state'; @Component({ selector: 'abp-nav-items', @@ -52,7 +52,7 @@ export class NavItemsComponent implements AfterViewInit, OnDestroy { rightPartElements: TemplateRef[] = []; - trackByFn: TrackByFunction = (_, element) => element; + trackByFn: TrackByFunction = (_, element) => element.name; get defaultLanguage$(): Observable { return this.languages$.pipe( diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html b/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html index 6437233cb1..4d22ddc8d2 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html @@ -1,6 +1,6 @@