diff --git a/npm/ng-packs/.prettierrc b/npm/ng-packs/.prettierrc index 4b9a2d97d0..3f584f6079 100644 --- a/npm/ng-packs/.prettierrc +++ b/npm/ng-packs/.prettierrc @@ -1,5 +1,4 @@ { "printWidth": 120, - "singleQuote": true, - "trailingComma": "all" + "singleQuote": true } diff --git a/npm/ng-packs/angular.json b/npm/ng-packs/angular.json index 0aa1c106a5..d9d0e21b62 100644 --- a/npm/ng-packs/angular.json +++ b/npm/ng-packs/angular.json @@ -27,7 +27,10 @@ "lint": { "builder": "@angular-devkit/build-angular:tslint", "options": { - "tsConfig": ["packages/core/tsconfig.lib.json", "packages/core/tsconfig.spec.json"], + "tsConfig": [ + "packages/core/tsconfig.lib.json", + "packages/core/tsconfig.spec.json" + ], "exclude": ["**/node_modules/**"] } } @@ -58,7 +61,10 @@ "lint": { "builder": "@angular-devkit/build-angular:tslint", "options": { - "tsConfig": ["packages/theme-shared/tsconfig.lib.json", "packages/theme-shared/tsconfig.spec.json"], + "tsConfig": [ + "packages/theme-shared/tsconfig.lib.json", + "packages/theme-shared/tsconfig.spec.json" + ], "exclude": ["**/node_modules/**"] } } @@ -89,7 +95,10 @@ "lint": { "builder": "@angular-devkit/build-angular:tslint", "options": { - "tsConfig": ["packages/theme-basic/tsconfig.lib.json", "packages/theme-basic/tsconfig.spec.json"], + "tsConfig": [ + "packages/theme-basic/tsconfig.lib.json", + "packages/theme-basic/tsconfig.spec.json" + ], "exclude": ["**/node_modules/**"] } } @@ -119,7 +128,10 @@ "lint": { "builder": "@angular-devkit/build-angular:tslint", "options": { - "tsConfig": ["packages/account/tsconfig.lib.json", "packages/account/tsconfig.spec.json"], + "tsConfig": [ + "packages/account/tsconfig.lib.json", + "packages/account/tsconfig.spec.json" + ], "exclude": ["**/node_modules/**"] } } @@ -182,7 +194,10 @@ "lint": { "builder": "@angular-devkit/build-angular:tslint", "options": { - "tsConfig": ["packages/identity/tsconfig.lib.json", "packages/identity/tsconfig.spec.json"], + "tsConfig": [ + "packages/identity/tsconfig.lib.json", + "packages/identity/tsconfig.spec.json" + ], "exclude": ["**/node_modules/**"] } } @@ -282,9 +297,7 @@ "packages/setting-management/tsconfig.lib.json", "packages/setting-management/tsconfig.spec.json" ], - "exclude": [ - "**/node_modules/**" - ] + "exclude": ["**/node_modules/**"] } } } @@ -317,9 +330,7 @@ "packages/setting-management-config/tsconfig.lib.json", "packages/setting-management-config/tsconfig.spec.json" ], - "exclude": [ - "**/node_modules/**" - ] + "exclude": ["**/node_modules/**"] } } } @@ -352,9 +363,7 @@ "packages/identity-config/tsconfig.lib.json", "packages/identity-config/tsconfig.spec.json" ], - "exclude": [ - "**/node_modules/**" - ] + "exclude": ["**/node_modules/**"] } } } @@ -387,9 +396,7 @@ "packages/account-config/tsconfig.lib.json", "packages/account-config/tsconfig.spec.json" ], - "exclude": [ - "**/node_modules/**" - ] + "exclude": ["**/node_modules/**"] } } } @@ -422,9 +429,7 @@ "packages/tenant-management-config/tsconfig.lib.json", "packages/tenant-management-config/tsconfig.spec.json" ], - "exclude": [ - "**/node_modules/**" - ] + "exclude": ["**/node_modules/**"] } } } diff --git a/npm/ng-packs/package.json b/npm/ng-packs/package.json index b40344ed62..5668bc118c 100644 --- a/npm/ng-packs/package.json +++ b/npm/ng-packs/package.json @@ -2,27 +2,29 @@ "scripts": { "ng": "ng", "symlink": "symlink", - "abpng": "abpng" + "abpng": "abpng", + "commit": "git-cz", + "lint": "ng lint --fix" }, "devDependencies": { "@abp/ng.core": "^0.9.0", "@abp/ng.feature-management": "^0.9.0", "@abp/ng.permission-management": "^0.9.0", "@abp/ng.theme.shared": "^0.9.0", - "@angular-devkit/build-angular": "~0.803.5", - "@angular-devkit/build-ng-packagr": "~0.803.5", - "@angular/animations": "~8.2.7", - "@angular/cdk": "^8.0.1", - "@angular/cli": "~8.3.5", - "@angular/common": "~8.1.2", - "@angular/compiler": "~8.2.7", - "@angular/compiler-cli": "~8.2.7", - "@angular/core": "~8.1.2", - "@angular/forms": "~8.1.2", - "@angular/language-service": "~8.2.7", - "@angular/platform-browser": "~8.2.7", - "@angular/platform-browser-dynamic": "~8.2.7", - "@angular/router": "~8.1.2", + "@angular-devkit/build-angular": "~0.803.6", + "@angular-devkit/build-ng-packagr": "~0.803.6", + "@angular/animations": "~8.2.8", + "@angular/cdk": "^8.2.2", + "@angular/cli": "~8.3.6", + "@angular/common": "~8.2.8", + "@angular/compiler-cli": "~8.2.8", + "@angular/compiler": "~8.2.8", + "@angular/core": "~8.2.8", + "@angular/forms": "~8.2.8", + "@angular/language-service": "~8.2.8", + "@angular/platform-browser-dynamic": "~8.2.8", + "@angular/platform-browser": "~8.2.8", + "@angular/router": "~8.2.8", "@ng-bootstrap/ng-bootstrap": "^5.1.0", "@ngneat/spectator": "^4.3.2", "@ngx-validate/core": "^0.0.5", @@ -35,32 +37,58 @@ "angular-oauth2-oidc": "^8.0.1", "bootstrap": "^4.3.1", "chart.js": "^2.8.0", - "codelyzer": "^5.0.0", + "codelyzer": "^5.1.2", + "cz-conventional-changelog": "3.0.2", "font-awesome": "^4.7.0", + "husky": "^3.0.7", "jasmine-core": "~3.4.0", "jasmine-spec-reporter": "~4.2.1", "just-clone": "3.1.0", "just-compare": "^1.3.0", - "karma": "~4.1.0", "karma-chrome-launcher": "~2.2.0", "karma-coverage-istanbul-reporter": "~2.0.1", - "karma-jasmine": "~2.0.1", "karma-jasmine-html-reporter": "^1.4.0", + "karma-jasmine": "~2.0.1", + "karma": "~4.1.0", + "lint-staged": "^9.4.0", "ng-packagr": "^5.5.0", "ngx-perfect-scrollbar": "^8.0.0", - "primeicons": "^1.0.0", + "ngxs-reset-plugin": "^1.2.0", + "prettier": "^1.18.2", + "primeicons": "^2.0.0", "primeng": "^8.0.0", "protractor": "~5.4.0", "rxjs": "~6.4.0", "snq": "^1.0.3", "symlink-manager": "^1.3.0", "ts-node": "~7.0.0", - "tsickle": "^0.35.0", - "tslint": "~5.15.0", + "tsickle": "^0.37.0", + "tslint": "~5.20.0", "typescript": "~3.5.3", "zone.js": "~0.9.1" }, "dependencies": { "tslib": "^1.10.0" + }, + "config": { + "commitizen": { + "path": "cz-conventional-changelog" + } + }, + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, + "lint-staged": { + "*.{ts,js,html,json,md,yaml,yml}": [ + "prettier --write", + "git add" + ], + "*.ts": [ + "prettier --write", + "tslint --fix", + "git add" + ] } } diff --git a/npm/ng-packs/packages/account/src/lib/components/tenant-box/tenant-box.component.ts b/npm/ng-packs/packages/account/src/lib/components/tenant-box/tenant-box.component.ts index a1ba6004c5..c114095adc 100644 --- a/npm/ng-packs/packages/account/src/lib/components/tenant-box/tenant-box.component.ts +++ b/npm/ng-packs/packages/account/src/lib/components/tenant-box/tenant-box.component.ts @@ -9,10 +9,14 @@ import { AccountService } from '../../services/account.service'; @Component({ selector: 'abp-tenant-box', - templateUrl: './tenant-box.component.html', + templateUrl: './tenant-box.component.html' }) export class TenantBoxComponent implements OnInit { - constructor(private store: Store, private toasterService: ToasterService, private accountService: AccountService) {} + constructor( + private store: Store, + private toasterService: ToasterService, + private accountService: AccountService + ) {} tenant = {} as ABP.BasicItem; @@ -21,7 +25,9 @@ export class TenantBoxComponent implements OnInit { isModalVisible: boolean; ngOnInit() { - this.tenant = this.store.selectSnapshot(SessionState.getTenant) || ({} as ABP.BasicItem); + this.tenant = + this.store.selectSnapshot(SessionState.getTenant) || + ({} as ABP.BasicItem); this.tenantName = this.tenant.name || ''; } @@ -37,24 +43,31 @@ export class TenantBoxComponent implements OnInit { take(1), catchError(err => { this.toasterService.error( - snq(() => err.error.error_description, 'AbpUi::DefaultErrorMessage'), - 'AbpUi::Error', + snq( + () => err.error.error_description, + 'AbpUi::DefaultErrorMessage' + ), + 'AbpUi::Error' ); return throwError(err); - }), + }) ) .subscribe(({ success, tenantId }) => { if (success) { this.tenant = { id: tenantId, - name: this.tenant.name, + name: this.tenant.name }; this.tenantName = this.tenant.name; this.isModalVisible = false; } else { - this.toasterService.error(`AbpUiMultiTenancy::GivenTenantIsNotAvailable`, 'AbpUi::Error', { - messageLocalizationParams: [this.tenant.name], - }); + this.toasterService.error( + 'AbpUiMultiTenancy::GivenTenantIsNotAvailable', + 'AbpUi::Error', + { + messageLocalizationParams: [this.tenant.name] + } + ); this.tenant = {} as ABP.BasicItem; } this.store.dispatch(new SetTenant(success ? this.tenant : null)); diff --git a/npm/ng-packs/packages/account/src/lib/services/account.service.ts b/npm/ng-packs/packages/account/src/lib/services/account.service.ts index ce90f7c6e0..5061cfeea1 100644 --- a/npm/ng-packs/packages/account/src/lib/services/account.service.ts +++ b/npm/ng-packs/packages/account/src/lib/services/account.service.ts @@ -4,7 +4,7 @@ import { RestService, Rest } from '@abp/ng.core'; import { RegisterResponse, RegisterRequest, TenantIdResponse } from '../models'; @Injectable({ - providedIn: 'root', + providedIn: 'root' }) export class AccountService { constructor(private rest: RestService) {} @@ -12,7 +12,7 @@ export class AccountService { findTenant(tenantName: string): Observable { const request: Rest.Request = { method: 'GET', - url: `/api/abp/multi-tenancy/tenants/by-name/${tenantName}`, + url: `/api/abp/multi-tenancy/tenants/by-name/${tenantName}` }; return this.rest.request(request); @@ -21,8 +21,8 @@ export class AccountService { register(body: RegisterRequest): Observable { const request: Rest.Request = { method: 'POST', - url: `/api/account/register`, - body, + url: '/api/account/register', + body }; return this.rest.request(request, { skipHandleError: true }); diff --git a/npm/ng-packs/packages/core/src/lib/abstracts/ng-model.component.ts b/npm/ng-packs/packages/core/src/lib/abstracts/ng-model.component.ts index 6cc720b011..4614011b92 100644 --- a/npm/ng-packs/packages/core/src/lib/abstracts/ng-model.component.ts +++ b/npm/ng-packs/packages/core/src/lib/abstracts/ng-model.component.ts @@ -1,13 +1,11 @@ import { ControlValueAccessor } from '@angular/forms'; import { ChangeDetectorRef, Component, Injector, Input, Type } from '@angular/core'; -@Component({ template: '' }) +@Component({ selector: 'abp-abstract-ng-model', template: '' }) export class AbstractNgModelComponent implements ControlValueAccessor { - @Input() - disabled: boolean; + @Input() disabled: boolean; - @Input() - set value(value: T) { + @Input() set value(value: T) { this._value = value; this.notifyValueChange(); } 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 876c476d5f..40954268e8 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 @@ -13,21 +13,19 @@ import { takeUntilDestroy } from '../utils/rxjs-utils'; selector: 'abp-dynamic-layout', template: ` - - `, + ` }) export class DynamicLayoutComponent implements OnDestroy { - @Select(ConfigState.getOne('requirements')) - requirements$: Observable; + @Select(ConfigState.getOne('requirements')) requirements$: Observable; layout: Type; constructor(private router: Router, private route: ActivatedRoute, private store: Store) { const { requirements: { layouts }, - routes, + routes } = this.store.selectSnapshot(ConfigState.getAll); if ((this.route.snapshot.data || {}).layout) { diff --git a/npm/ng-packs/packages/core/src/lib/directives/autofocus.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/autofocus.directive.ts index edd7c8497a..7fb7de34d1 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/autofocus.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/autofocus.directive.ts @@ -1,11 +1,12 @@ import { Directive, ElementRef, Input, AfterViewInit } from '@angular/core'; @Directive({ - selector: '[autofocus]', + // tslint:disable-next-line: directive-selector + selector: '[autofocus]' }) export class AutofocusDirective implements AfterViewInit { @Input('autofocus') - delay: number = 0; + delay = 0; constructor(private elRef: ElementRef) {} diff --git a/npm/ng-packs/packages/core/src/lib/directives/debounce.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/debounce.directive.ts index 144c43d4e2..fc29b5fd65 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/debounce.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/debounce.directive.ts @@ -4,12 +4,13 @@ import { debounceTime } from 'rxjs/operators'; import { takeUntilDestroy } from '@ngx-validate/core'; @Directive({ - selector: '[input.debounce]', + // tslint:disable-next-line: directive-selector + selector: '[input.debounce]' }) export class InputEventDebounceDirective implements OnInit { - @Input() debounce: number = 300; + @Input() debounce = 300; - @Output('input.debounce') debounceEvent = new EventEmitter(); + @Output('input.debounce') readonly debounceEvent = new EventEmitter(); constructor(private renderer: Renderer2, private el: ElementRef) {} @@ -17,7 +18,7 @@ export class InputEventDebounceDirective implements OnInit { fromEvent(this.el.nativeElement, 'input') .pipe( debounceTime(this.debounce), - takeUntilDestroy(this), + takeUntilDestroy(this) ) .subscribe((event: Event) => { this.debounceEvent.emit(event); diff --git a/npm/ng-packs/packages/core/src/lib/directives/for.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/for.directive.ts index 604f7a8660..932ff11b8e 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/for.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/for.directive.ts @@ -9,7 +9,7 @@ import { OnChanges, TemplateRef, TrackByFunction, - ViewContainerRef, + ViewContainerRef } from '@angular/core'; import compare from 'just-compare'; import clone from 'just-clone'; @@ -25,7 +25,7 @@ class RecordView { } @Directive({ - selector: '[abpFor]', + selector: '[abpFor]' }) export class ForDirective implements OnChanges { @Input('abpForOf') @@ -67,7 +67,7 @@ export class ForDirective implements OnChanges { constructor( private tempRef: TemplateRef, private vcRef: ViewContainerRef, - private differs: IterableDiffers, + private differs: IterableDiffers ) {} private iterateOverAppliedOperations(changes: IterableChanges) { @@ -78,7 +78,7 @@ export class ForDirective implements OnChanges { const view = this.vcRef.createEmbeddedView( this.tempRef, new AbpForContext(null, -1, -1, this.items), - currentIndex, + currentIndex ); rw.push(new RecordView(record, view)); @@ -113,8 +113,11 @@ export class ForDirective implements OnChanges { private projectItems(items: any[]): void { if (!items.length && this.emptyRef) { + this.vcRef.clear(); + // tslint:disable-next-line: no-unused-expression this.vcRef.createEmbeddedView(this.emptyRef).rootNodes; this.isShowEmptyRef = true; + this.differ = null; return; } diff --git a/npm/ng-packs/packages/core/src/lib/directives/form-submit.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/form-submit.directive.ts index c77016ce7f..00982bbbb9 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/form-submit.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/form-submit.directive.ts @@ -7,7 +7,7 @@ import { OnDestroy, OnInit, Output, - Self, + Self } from '@angular/core'; import { FormControl, FormGroup, FormGroupDirective } from '@angular/forms'; import { fromEvent } from 'rxjs'; @@ -17,21 +17,21 @@ import { takeUntilDestroy } from '../utils'; type Controls = { [key: string]: FormControl } | FormGroup[]; @Directive({ - selector: 'form[ngSubmit][formGroup]', + // tslint:disable-next-line: directive-selector + selector: 'form[ngSubmit][formGroup]' }) export class FormSubmitDirective implements OnInit, OnDestroy { @Input() notValidateOnSubmit: string | boolean; - @Output() - ngSubmit = new EventEmitter(); + @Output() readonly ngSubmit = new EventEmitter(); - executedNgSubmit: boolean = false; + executedNgSubmit = false; constructor( @Self() private formGroupDirective: FormGroupDirective, private host: ElementRef, - private cdRef: ChangeDetectorRef, + private cdRef: ChangeDetectorRef ) {} ngOnInit() { @@ -44,7 +44,7 @@ export class FormSubmitDirective implements OnInit, OnDestroy { .pipe( debounceTime(200), filter((key: KeyboardEvent) => key && key.key === 'Enter'), - takeUntilDestroy(this), + takeUntilDestroy(this) ) .subscribe(() => { if (!this.executedNgSubmit) { @@ -57,7 +57,7 @@ export class FormSubmitDirective implements OnInit, OnDestroy { fromEvent(this.host.nativeElement, 'submit') .pipe( takeUntilDestroy(this), - filter(() => !this.notValidateOnSubmit && typeof this.notValidateOnSubmit !== 'string'), + filter(() => !this.notValidateOnSubmit && typeof this.notValidateOnSubmit !== 'string') ) .subscribe(() => { if (!this.executedNgSubmit) { diff --git a/npm/ng-packs/packages/core/src/lib/directives/stop-propagation.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/stop-propagation.directive.ts index ffa2e9e10b..7b9ae46e9c 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/stop-propagation.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/stop-propagation.directive.ts @@ -3,10 +3,11 @@ import { fromEvent } from 'rxjs'; import { takeUntilDestroy } from '@ngx-validate/core'; @Directive({ - selector: '[click.stop]', + // tslint:disable-next-line: directive-selector + selector: '[click.stop]' }) export class ClickEventStopPropagationDirective implements OnInit { - @Output('click.stop') stopPropEvent = new EventEmitter(); + @Output('click.stop') readonly stopPropEvent = new EventEmitter(); constructor(private renderer: Renderer2, private el: ElementRef) {} diff --git a/npm/ng-packs/packages/core/src/lib/directives/visibility.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/visibility.directive.ts index 56b49f23ee..2428b736a3 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/visibility.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/visibility.directive.ts @@ -3,14 +3,14 @@ import { Subject } from 'rxjs'; import snq from 'snq'; @Directive({ - selector: '[abpVisibility]', + selector: '[abpVisibility]' }) export class VisibilityDirective implements AfterViewInit { @Input('abpVisibility') focusedElement: HTMLElement; @Input() - mutationObserverEnabled: boolean = true; + mutationObserverEnabled = true; completed$ = new Subject(); @@ -25,7 +25,7 @@ export class VisibilityDirective implements AfterViewInit { const htmlNodes = snq( () => Array.from(mutation.target.childNodes).filter(node => node instanceof HTMLElement), - [], + [] ); if (!htmlNodes.length) { @@ -40,13 +40,13 @@ export class VisibilityDirective implements AfterViewInit { }); observer.observe(this.focusedElement, { - childList: true, + childList: true }); } else { setTimeout(() => { const htmlNodes = snq( () => Array.from(this.focusedElement.childNodes).filter(node => node instanceof HTMLElement), - [], + [] ); if (!htmlNodes.length) this.removeFromDOM(); diff --git a/npm/ng-packs/packages/core/src/lib/pipes/index.ts b/npm/ng-packs/packages/core/src/lib/pipes/index.ts index 4802a25ca4..eb028eee4f 100644 --- a/npm/ng-packs/packages/core/src/lib/pipes/index.ts +++ b/npm/ng-packs/packages/core/src/lib/pipes/index.ts @@ -1,2 +1,2 @@ export * from './localization.pipe'; -export * from "./sort.pipe"; +export * from './sort.pipe'; diff --git a/npm/ng-packs/packages/core/src/lib/pipes/localization.pipe.ts b/npm/ng-packs/packages/core/src/lib/pipes/localization.pipe.ts index 0766a50091..2050e742cf 100644 --- a/npm/ng-packs/packages/core/src/lib/pipes/localization.pipe.ts +++ b/npm/ng-packs/packages/core/src/lib/pipes/localization.pipe.ts @@ -7,10 +7,11 @@ import { Subject } from 'rxjs'; @Pipe({ name: 'abpLocalization', - pure: false, // required to update the value + // tslint:disable-next-line: no-pipe-impure + pure: false // required to update the value }) export class LocalizationPipe implements PipeTransform, OnDestroy { - initialValue: string = ''; + initialValue = ''; value: string; @@ -27,13 +28,13 @@ export class LocalizationPipe implements PipeTransform, OnDestroy { .select( ConfigState.getCopy( value, - ...interpolateParams.reduce((acc, val) => (Array.isArray(val) ? [...acc, ...val] : [...acc, val]), []), - ), + ...interpolateParams.reduce((acc, val) => (Array.isArray(val) ? [...acc, ...val] : [...acc, val]), []) + ) ) .pipe( takeUntil(this.destroy$), takeUntilDestroy(this), - distinctUntilChanged(), + distinctUntilChanged() ) .subscribe(copy => (this.value = copy)); } diff --git a/npm/ng-packs/packages/core/src/lib/pipes/sort.pipe.ts b/npm/ng-packs/packages/core/src/lib/pipes/sort.pipe.ts index b44de74193..09cf1d7147 100644 --- a/npm/ng-packs/packages/core/src/lib/pipes/sort.pipe.ts +++ b/npm/ng-packs/packages/core/src/lib/pipes/sort.pipe.ts @@ -1,13 +1,14 @@ import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ - name: 'abpSort', - pure: false + name: 'abpSort', + // tslint:disable-next-line: no-pipe-impure + pure: false }) export class SortPipe implements PipeTransform { - transform(value: any[], sortOrder: string): any { - sortOrder = sortOrder.toLowerCase(); - if(sortOrder === "desc") return value.reverse(); - else return value; - } -} \ No newline at end of file + transform(value: any[], sortOrder: string): any { + sortOrder = sortOrder.toLowerCase(); + if (sortOrder === 'desc') return value.reverse(); + else return value; + } +} diff --git a/npm/ng-packs/packages/core/src/lib/plugins/config/config.plugin.ts b/npm/ng-packs/packages/core/src/lib/plugins/config/config.plugin.ts index c65955f877..12c2b0700a 100644 --- a/npm/ng-packs/packages/core/src/lib/plugins/config/config.plugin.ts +++ b/npm/ng-packs/packages/core/src/lib/plugins/config/config.plugin.ts @@ -12,7 +12,7 @@ export let ABP_ROUTES = [] as ABP.FullRoute[]; @Injectable() export class ConfigPlugin implements NgxsPlugin { - private initialized: boolean = false; + private initialized = false; constructor(@Inject(NGXS_CONFIG_PLUGIN_OPTIONS) private options: ABP.Root, private router: Router) {} @@ -22,14 +22,17 @@ export class ConfigPlugin implements NgxsPlugin { // const layouts = snq(() => this.options.requirements.layouts.filter(layout => layout instanceof Type), []); if (isInitAction && !this.initialized) { - let { routes, wrappers } = transformRoutes(this.router.config); + const transformedRoutes = transformRoutes(this.router.config); + let { routes } = transformedRoutes; + const { wrappers } = transformedRoutes; + routes = organizeRoutes(routes, wrappers); const flattedRoutes = flatRoutes(clone(routes)); state = setValue(state, 'ConfigState', { ...(state.ConfigState && { ...state.ConfigState }), ...this.options, routes, - flattedRoutes, + flattedRoutes }); this.initialized = true; @@ -49,6 +52,7 @@ function transformRoutes(routes: Routes = [], wrappers: ABP.FullRoute[] = []): a return snq(() => route.data.routes.routes.find(r => r.path === route.path), false); }) .reduce((acc, val) => [...acc, ...val.data.routes.routes], []); + // tslint:disable-next-line: deprecation ABP_ROUTES = [...ABP_ROUTES, ...abpRoutes]; wrappers = ABP_ROUTES.filter(ar => ar.wrapper); @@ -69,7 +73,7 @@ function transformRoutes(routes: Routes = [], wrappers: ABP.FullRoute[] = []): a ...route.data.routes, path: route.path, name: snq(() => route.data.routes.name, route.path), - children: route.data.routes.children || [], + children: route.data.routes.children || [] } as ABP.FullRoute); } }); @@ -86,8 +90,8 @@ function setUrls(routes: ABP.FullRoute[], parentUrl?: string): ABP.FullRoute[] { url: `${parentUrl}/${route.path}`, ...(route.children && route.children.length && { - children: setUrls(route.children, `${parentUrl}/${route.path}`), - }), + children: setUrls(route.children, `${parentUrl}/${route.path}`) + }) })); } @@ -96,8 +100,8 @@ function setUrls(routes: ABP.FullRoute[], parentUrl?: string): ABP.FullRoute[] { url: `/${route.path}`, ...(route.children && route.children.length && { - children: setUrls(route.children, `/${route.path}`), - }), + children: setUrls(route.children, `/${route.path}`) + }) })); } 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 8225aaea2a..4f43a0bf65 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 @@ -11,7 +11,7 @@ import { setChildRoute, sortRoutes, organizeRoutes } from '../utils/route-utils' @State({ name: 'ConfigState', - defaults: {} as Config.State, + defaults: {} as Config.State }) export class ConfigState { @Selector() @@ -27,9 +27,9 @@ export class ConfigState { static getOne(key: string) { const selector = createSelector( [ConfigState], - function(state: Config.State) { + (state: Config.State) => { return state[key]; - }, + } ); return selector; @@ -46,7 +46,7 @@ export class ConfigState { const selector = createSelector( [ConfigState], - function(state: Config.State) { + (state: Config.State) => { return (keys as string[]).reduce((acc, val) => { if (acc) { return acc[val]; @@ -54,7 +54,7 @@ export class ConfigState { return undefined; }, state); - }, + } ); return selector; @@ -63,7 +63,7 @@ export class ConfigState { static getRoute(path?: string, name?: string) { const selector = createSelector( [ConfigState], - function(state: Config.State) { + (state: Config.State) => { const { flattedRoutes } = state; return (flattedRoutes as ABP.FullRoute[]).find(route => { if (path && route.path === path) { @@ -72,7 +72,7 @@ export class ConfigState { return route; } }); - }, + } ); return selector; @@ -81,9 +81,9 @@ export class ConfigState { static getApiUrl(key?: string) { const selector = createSelector( [ConfigState], - function(state: Config.State): string { + (state: Config.State): string => { return state.environment.apis[key || 'default'].url; - }, + } ); return selector; @@ -92,9 +92,9 @@ export class ConfigState { static getSetting(key: string) { const selector = createSelector( [ConfigState], - function(state: Config.State) { + (state: Config.State) => { return snq(() => state.setting.values[key]); - }, + } ); return selector; @@ -103,10 +103,10 @@ export class ConfigState { static getGrantedPolicy(key: string) { const selector = createSelector( [ConfigState], - function(state: Config.State): boolean { + (state: Config.State): boolean => { if (!key) return true; return snq(() => state.auth.grantedPolicies[key], false); - }, + } ); return selector; @@ -118,20 +118,20 @@ export class ConfigState { const keys = key.split('::') as string[]; const selector = createSelector( [ConfigState], - function(state: Config.State) { + (state: Config.State) => { if (!state.localization) return key; const { defaultResourceName } = state.environment.localization; if (keys[0] === '') { if (!defaultResourceName) { throw new Error( - `Please check your environment. May you forget set defaultResourceName? + `Please check your environment. May you forget set defaultResourceName? Here is the example: { production: false, localization: { defaultResourceName: 'MyProjectName' } - }`, + }` ); } @@ -154,7 +154,7 @@ export class ConfigState { } return copy || key; - }, + } ); return selector; @@ -167,8 +167,8 @@ export class ConfigState { return this.appConfigurationService.getConfiguration().pipe( tap(configuration => patchState({ - ...configuration, - }), + ...configuration + }) ), switchMap(configuration => { let defaultLang: string = configuration.setting.values['Abp.Localization.DefaultLanguage']; @@ -178,7 +178,7 @@ export class ConfigState { } return this.store.selectSnapshot(SessionState.getLanguage) ? of(null) : dispatch(new SetLanguage(defaultLang)); - }), + }) ); } @@ -191,7 +191,7 @@ export class ConfigState { routes = patchRouteDeep(routes, name, newValue); return patchState({ - routes, + routes }); } } @@ -200,7 +200,7 @@ function patchRouteDeep( routes: ABP.FullRoute[], name: string, newValue: Partial, - parentUrl: string = null, + parentUrl: string = null ): ABP.FullRoute[] { routes = routes.map(route => { if (route.name === name) { @@ -211,7 +211,7 @@ function patchRouteDeep( if (newValue.children && newValue.children.length) { newValue.children = newValue.children.map(child => ({ ...child, - url: `${parentUrl}/${route.path}/${child.path}`, + url: `${parentUrl}/${route.path}/${child.path}` })); } 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 new file mode 100644 index 0000000000..0116d6ad59 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts @@ -0,0 +1,154 @@ +import { ConfigState, CoreModule, DynamicLayoutComponent, eLayoutType, ABP, RouterOutletComponent } from '@abp/ng.core'; +import { Location } from '@angular/common'; +import { Component } from '@angular/core'; +import { createRoutingFactory, SpectatorRouting, SpyObject } from '@ngneat/spectator'; +import { NgxsModule, Store } from '@ngxs/store'; +import { LAYOUTS, ThemeBasicModule } from '../../../../theme-basic/src/public-api'; +import { OAuthService } from 'angular-oauth2-oidc'; +import { NgxsResetPluginModule, StateOverwrite } from 'ngxs-reset-plugin'; +import { ThemeSharedModule } from '../../../../theme-shared/src/public-api'; +import { ActivatedRoute } from '@angular/router'; + +@Component({ + selector: 'abp-dummy', + template: '{{route.snapshot.data?.name}} works!' +}) +class DummyComponent { + constructor(public route: ActivatedRoute) {} +} + +describe('DynamicLayoutComponent', () => { + const createComponent = createRoutingFactory({ + component: RouterOutletComponent, + declareComponent: false, + imports: [ + CoreModule, + NgxsModule.forRoot([ConfigState]), + NgxsResetPluginModule.forRoot(), + ThemeSharedModule.forRoot(), + ThemeBasicModule + ], + declarations: [DummyComponent], + stubsEnabled: false, + providers: [{ provide: OAuthService, useValue: { getAccessToken: () => true } }], + routes: [ + { path: '', component: RouterOutletComponent }, + { + path: 'parentWithLayout', + component: DynamicLayoutComponent, + children: [ + { + path: 'childWithoutLayout', + component: DummyComponent, + data: { name: 'childWithoutLayout' } + }, + { + path: 'childWithLayout', + component: DummyComponent, + data: { name: 'childWithLayout' } + } + ] + }, + { + path: 'withData', + component: DynamicLayoutComponent, + children: [ + { + path: '', + component: DummyComponent, + data: { name: 'withData' } + } + ], + data: { layout: eLayoutType.empty } + }, + { + path: 'withoutLayout', + component: DynamicLayoutComponent, + children: [ + { + path: '', + component: DummyComponent, + data: { name: 'withoutLayout' } + } + ], + data: { layout: null } + } + ] + }); + + let spectator: SpectatorRouting; + let store: SpyObject; + + beforeEach(async () => { + spectator = createComponent(); + store = spectator.get(Store); + store.dispatch( + new StateOverwrite([ + ConfigState, + { + requirements: { layouts: LAYOUTS }, + 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: {} } + } + ]) + ); + }); + + it('should handle application layout from parent abp route and display it', async () => { + spectator.router.navigateByUrl('/parentWithLayout/childWithoutLayout'); + await spectator.fixture.whenStable(); + spectator.detectComponentChanges(); + expect(spectator.query('abp-dynamic-layout')).toBeTruthy(); + expect(spectator.query('abp-layout-application')).toBeTruthy(); + }); + + it('should handle account layout from own property and display it', async () => { + spectator.router.navigateByUrl('/parentWithLayout/childWithLayout'); + await spectator.fixture.whenStable(); + spectator.detectComponentChanges(); + expect(spectator.query('abp-layout-account')).toBeTruthy(); + }); + + it('should handle empty layout from route data and display it', async () => { + spectator.router.navigateByUrl('/withData'); + await spectator.fixture.whenStable(); + spectator.detectComponentChanges(); + expect(spectator.query('abp-layout-empty')).toBeTruthy(); + }); + + it('should display empty layout when layout is null', async () => { + spectator.router.navigateByUrl('/withoutLayout'); + await spectator.fixture.whenStable(); + spectator.detectComponentChanges(); + expect(spectator.query('abp-layout-empty')).toBeTruthy(); + }); + + it('should not display any layout when layouts are empty', async () => { + store.dispatch( + new StateOverwrite([ConfigState, { ...store.selectSnapshot(ConfigState), requirements: { layouts: [] } }]) + ); + + spectator.detectChanges(); + + spectator.router.navigateByUrl('/withoutLayout'); + await spectator.fixture.whenStable(); + spectator.detectComponentChanges(); + + expect(spectator.query('abp-layout-empty')).toBeFalsy(); + expect(spectator.query('abp-dynamic-layout').children[0].tagName).toEqual('ROUTER-OUTLET'); + }); +}); diff --git a/npm/ng-packs/packages/core/src/lib/tests/router-outlet.component.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/router-outlet.component.spec.ts new file mode 100644 index 0000000000..42c5a0a9c6 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tests/router-outlet.component.spec.ts @@ -0,0 +1,15 @@ +import { Spectator, createComponentFactory, createHostFactory } from '@ngneat/spectator'; +import { RouterOutletComponent } from '@abp/ng.core'; +import { RouterTestingModule } from '@angular/router/testing'; + +describe('RouterOutletComponent', () => { + let spectator: Spectator; + const createHost = createHostFactory({ component: RouterOutletComponent, imports: [RouterTestingModule] }); + + it('should have a router-outlet element', () => { + spectator = createHost(''); + console.log((spectator.debugElement.nativeElement as HTMLElement).children); + expect((spectator.debugElement.nativeElement as HTMLElement).children.length).toBe(1); + expect((spectator.debugElement.nativeElement as HTMLElement).children[0].tagName).toBe('ROUTER-OUTLET'); + }); +}); diff --git a/npm/ng-packs/packages/core/src/lib/utils/common-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/common-utils.ts index ba76b0f97f..756d3a34dd 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/common-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/common-utils.ts @@ -1,4 +1,5 @@ export function noop() { + // tslint:disable-next-line: only-arrow-functions const fn = function() {}; return fn; } diff --git a/npm/ng-packs/packages/core/src/lib/utils/generator-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/generator-utils.ts index 4e6bf5e09c..4256489efa 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/generator-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/generator-utils.ts @@ -1,5 +1,6 @@ export function uuid(a?: any): string { return a - ? (a ^ ((Math.random() * 16) >> (a / 4))).toString(16) + ? // tslint:disable-next-line: no-bitwise + (a ^ ((Math.random() * 16) >> (a / 4))).toString(16) : ('' + 1e7 + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, uuid); } diff --git a/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts index 7cf870667b..dc4ae36065 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts @@ -6,7 +6,7 @@ import differentLocales from '../constants/different-locales'; import { SessionState } from '../states/session.state'; export function getInitialData(injector: Injector) { - const fn = function() { + const fn = () => { const store: Store = injector.get(Store); return store.dispatch(new GetAppConfiguration()).toPromise(); @@ -16,7 +16,7 @@ export function getInitialData(injector: Injector) { } export function localeInitializer(injector: Injector) { - const fn = function() { + const fn = () => { const store: Store = injector.get(Store); const lang = store.selectSnapshot(SessionState.getLanguage) || 'en'; diff --git a/npm/ng-packs/packages/core/src/lib/utils/rxjs-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/rxjs-utils.ts index 533e4cc30d..ef6a75fe65 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/rxjs-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/rxjs-utils.ts @@ -6,18 +6,19 @@ function isFunction(value) { } export const takeUntilDestroy = (componentInstance, destroyMethodName = 'ngOnDestroy') => ( - source: Observable, + source: Observable ) => { const originalDestroy = componentInstance[destroyMethodName]; if (isFunction(originalDestroy) === false) { throw new Error( - `${componentInstance.constructor.name} is using untilDestroyed but doesn't implement ${destroyMethodName}`, + `${componentInstance.constructor.name} is using untilDestroyed but doesn't implement ${destroyMethodName}` ); } if (!componentInstance['__takeUntilDestroy']) { componentInstance['__takeUntilDestroy'] = new Subject(); componentInstance[destroyMethodName] = function() { + // tslint:disable-next-line: no-unused-expression isFunction(originalDestroy) && originalDestroy.apply(this, arguments); componentInstance['__takeUntilDestroy'].next(true); componentInstance['__takeUntilDestroy'].complete(); diff --git a/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts b/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts index 38219b2b08..bf8e5b2e5e 100644 --- a/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts +++ b/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts @@ -9,7 +9,7 @@ import { pluck, tap } from 'rxjs/operators'; @Component({ selector: 'abp-feature-management', - templateUrl: './feature-management.component.html', + templateUrl: './feature-management.component.html' }) export class FeatureManagementComponent { @Input() @@ -32,13 +32,12 @@ export class FeatureManagementComponent { if (value) this.openModal(); } - @Output() - visibleChange = new EventEmitter(); + @Output() readonly visibleChange = new EventEmitter(); @Select(FeatureManagementState.getFeatures) features$: Observable; - modalBusy: boolean = false; + modalBusy = false; form: FormGroup; @@ -54,7 +53,12 @@ export class FeatureManagementComponent { getFeatures() { this.store - .dispatch(new GetFeatures({ providerKey: this.providerKey, providerName: this.providerName })) + .dispatch( + new GetFeatures({ + providerKey: this.providerKey, + providerName: this.providerName + }) + ) .pipe(pluck('FeatureManagementState', 'features')) .subscribe(features => { this.buildForm(features); @@ -78,7 +82,7 @@ export class FeatureManagementComponent { features = features.map((feature, i) => ({ name: feature.name, - value: !this.form.value[i] || this.form.value[i] === 'false' ? null : this.form.value[i], + value: !this.form.value[i] || this.form.value[i] === 'false' ? null : this.form.value[i] })); this.store @@ -86,8 +90,8 @@ export class FeatureManagementComponent { new UpdateFeatures({ providerKey: this.providerKey, providerName: this.providerName, - features, - }), + features + }) ) .subscribe(() => { this.modalBusy = false; diff --git a/npm/ng-packs/packages/feature-management/src/lib/states/index.ts b/npm/ng-packs/packages/feature-management/src/lib/states/index.ts index 5c32d87889..3c9c6e26d6 100644 --- a/npm/ng-packs/packages/feature-management/src/lib/states/index.ts +++ b/npm/ng-packs/packages/feature-management/src/lib/states/index.ts @@ -1 +1 @@ -export * from "./feature-management.state"; +export * from './feature-management.state'; diff --git a/npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.ts b/npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.ts index a13ffb7489..7ebda6be18 100644 --- a/npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.ts +++ b/npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.ts @@ -11,7 +11,7 @@ import { IdentityState } from '../../states/identity.state'; @Component({ selector: 'abp-roles', - templateUrl: './roles.component.html', + templateUrl: './roles.component.html' }) export class RolesComponent { @Select(IdentityState.getRoles) @@ -26,19 +26,19 @@ export class RolesComponent { isModalVisible: boolean; - visiblePermissions: boolean = false; + visiblePermissions = false; providerKey: string; pageQuery: ABP.PageQueryParams = { - sorting: 'name', + sorting: 'name' }; - loading: boolean = false; + loading = false; - modalBusy: boolean = false; + modalBusy = false; - sortOrder: string = 'asc'; + sortOrder = 'asc'; @ViewChild('modalContent', { static: false }) modalContent: TemplateRef; @@ -54,10 +54,10 @@ export class RolesComponent { this.form = this.fb.group({ name: new FormControl({ value: this.selected.name || '', disabled: this.selected.isStatic }, [ Validators.required, - Validators.maxLength(256), + Validators.maxLength(256) ]), isDefault: [this.selected.isDefault || false], - isPublic: [this.selected.isPublic || false], + isPublic: [this.selected.isPublic || false] }); } @@ -89,7 +89,7 @@ export class RolesComponent { .dispatch( this.selected.id ? new UpdateRole({ ...this.form.value, id: this.selected.id }) - : new CreateRole(this.form.value), + : new CreateRole(this.form.value) ) .subscribe(() => { this.modalBusy = false; @@ -100,7 +100,7 @@ export class RolesComponent { delete(id: string, name: string) { this.confirmationService .warn('AbpIdentity::RoleDeletionConfirmationMessage', 'AbpIdentity::AreYouSure', { - messageLocalizationParams: [name], + messageLocalizationParams: [name] }) .subscribe((status: Toaster.Status) => { if (status === Toaster.Status.confirm) { diff --git a/npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts b/npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts index f3a1891018..18f7507f29 100644 --- a/npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts +++ b/npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts @@ -12,13 +12,13 @@ import { GetUserById, GetUserRoles, GetUsers, - UpdateUser, + UpdateUser } from '../../actions/identity.actions'; import { Identity } from '../../models/identity'; import { IdentityState } from '../../states/identity.state'; @Component({ selector: 'abp-users', - templateUrl: './users.component.html', + templateUrl: './users.component.html' }) export class UsersComponent { @Select(IdentityState.getUsers) @@ -38,21 +38,21 @@ export class UsersComponent { roles: Identity.RoleItem[]; - visiblePermissions: boolean = false; + visiblePermissions = false; providerKey: string; pageQuery: ABP.PageQueryParams = { - sorting: 'userName', + sorting: 'userName' }; isModalVisible: boolean; - loading: boolean = false; + loading = false; - modalBusy: boolean = false; + modalBusy = false; - sortOrder: string = 'asc'; + sortOrder = 'asc'; trackByFn: TrackByFunction = (index, item) => Object.keys(item)[0] || index; @@ -80,10 +80,10 @@ export class UsersComponent { roleNames: this.fb.array( this.roles.map(role => this.fb.group({ - [role.name]: [!!snq(() => this.selectedUserRoles.find(userRole => userRole.id === role.id))], - }), - ), - ), + [role.name]: [!!snq(() => this.selectedUserRoles.find(userRole => userRole.id === role.id))] + }) + ) + ) }); if (!this.selected.userName) { this.form.addControl('password', new FormControl('', [Validators.required, Validators.maxLength(32)])); @@ -107,7 +107,7 @@ export class UsersComponent { .pipe( switchMap(() => this.store.dispatch(new GetUserRoles(id))), pluck('IdentityState'), - take(1), + take(1) ) .subscribe((state: Identity.State) => { this.selected = state.selectedUser; @@ -123,7 +123,7 @@ export class UsersComponent { const { roleNames } = this.form.value; const mappedRoleNames = snq( () => roleNames.filter(role => !!role[Object.keys(role)[0]]).map(role => Object.keys(role)[0]), - [], + [] ); this.store @@ -132,12 +132,12 @@ export class UsersComponent { ? new UpdateUser({ ...this.form.value, id: this.selected.id, - roleNames: mappedRoleNames, + roleNames: mappedRoleNames }) : new CreateUser({ ...this.form.value, - roleNames: mappedRoleNames, - }), + roleNames: mappedRoleNames + }) ) .subscribe(() => { this.modalBusy = false; @@ -148,7 +148,7 @@ export class UsersComponent { delete(id: string, userName: string) { this.confirmationService .warn('AbpIdentity::UserDeletionConfirmationMessage', 'AbpIdentity::AreYouSure', { - messageLocalizationParams: [userName], + messageLocalizationParams: [userName] }) .subscribe((status: Toaster.Status) => { if (status === Toaster.Status.confirm) { diff --git a/npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts b/npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts index 4b83c5a5ab..c968df1e3d 100644 --- a/npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts +++ b/npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts @@ -7,7 +7,7 @@ import { Output, Renderer2, SimpleChanges, - TrackByFunction, + TrackByFunction } from '@angular/core'; import { Select, Store } from '@ngxs/store'; import { from, Observable } from 'rxjs'; @@ -16,11 +16,13 @@ import { GetPermissions, UpdatePermissions } from '../actions/permission-managem import { PermissionManagement } from '../models/permission-management'; import { PermissionManagementState } from '../states/permission-management.state'; -type PermissionWithMargin = PermissionManagement.Permission & { margin: number }; +type PermissionWithMargin = PermissionManagement.Permission & { + margin: number; +}; @Component({ selector: 'abp-permission-management', - templateUrl: './permission-management.component.html', + templateUrl: './permission-management.component.html' }) export class PermissionManagementComponent implements OnInit, OnChanges { @Input() @@ -47,8 +49,7 @@ export class PermissionManagementComponent implements OnInit, OnChanges { } } - @Output() - visibleChange = new EventEmitter(); + @Output() readonly visibleChange = new EventEmitter(); @Select(PermissionManagementState.getPermissionGroups) groups$: Observable; @@ -60,18 +61,18 @@ export class PermissionManagementComponent implements OnInit, OnChanges { permissions: PermissionManagement.Permission[] = []; - selectThisTab: boolean = false; + selectThisTab = false; - selectAllTab: boolean = false; + selectAllTab = false; - modalBusy: boolean = false; + modalBusy = false; trackByFn: TrackByFunction = (_, item) => item.name; get selectedGroupPermissions$(): Observable { return this.groups$.pipe( map(groups => - this.selectedGroup ? groups.find(group => group.name === this.selectedGroup.name).permissions : [], + this.selectedGroup ? groups.find(group => group.name === this.selectedGroup.name).permissions : [] ), map(permissions => permissions.map( @@ -79,10 +80,10 @@ export class PermissionManagementComponent implements OnInit, OnChanges { (({ ...permission, margin: findMargin(permissions, permission), - isGranted: this.permissions.find(per => per.name === permission.name).isGranted, - } as any) as PermissionWithMargin), - ), - ), + isGranted: this.permissions.find(per => per.name === permission.name).isGranted + } as any) as PermissionWithMargin) + ) + ) ); } @@ -164,7 +165,7 @@ export class PermissionManagementComponent implements OnInit, OnChanges { this.permissions = [ ...this.permissions.slice(0, index), { ...this.permissions[index], isGranted: !this.selectThisTab }, - ...this.permissions.slice(index + 1), + ...this.permissions.slice(index + 1) ]; }); }); @@ -173,7 +174,10 @@ export class PermissionManagementComponent implements OnInit, OnChanges { } onClickSelectAll() { - this.permissions = this.permissions.map(permission => ({ ...permission, isGranted: !this.selectAllTab })); + this.permissions = this.permissions.map(permission => ({ + ...permission, + isGranted: !this.selectAllTab + })); this.selectThisTab = !this.selectAllTab; } @@ -186,12 +190,12 @@ export class PermissionManagementComponent implements OnInit, OnChanges { submit() { this.modalBusy = true; const unchangedPermissions = getPermissions( - this.store.selectSnapshot(PermissionManagementState.getPermissionGroups), + this.store.selectSnapshot(PermissionManagementState.getPermissionGroups) ); const changedPermissions: PermissionManagement.MinimumPermission[] = this.permissions .filter(per => - unchangedPermissions.find(unchanged => unchanged.name === per.name).isGranted === per.isGranted ? false : true, + unchangedPermissions.find(unchanged => unchanged.name === per.name).isGranted === per.isGranted ? false : true ) .map(({ name, isGranted }) => ({ name, isGranted })); @@ -201,8 +205,8 @@ export class PermissionManagementComponent implements OnInit, OnChanges { new UpdatePermissions({ providerKey: this.providerKey, providerName: this.providerName, - permissions: changedPermissions, - }), + permissions: changedPermissions + }) ) .subscribe(() => { this.modalBusy = false; @@ -220,7 +224,12 @@ export class PermissionManagementComponent implements OnInit, OnChanges { } this.store - .dispatch(new GetPermissions({ providerKey: this.providerKey, providerName: this.providerName })) + .dispatch( + new GetPermissions({ + providerKey: this.providerKey, + providerName: this.providerName + }) + ) .pipe(pluck('PermissionManagementState', 'permissionRes')) .subscribe((permissionRes: PermissionManagement.Response) => { this.selectedGroup = permissionRes.groups[0]; diff --git a/npm/ng-packs/packages/permission-management/src/lib/states/index.ts b/npm/ng-packs/packages/permission-management/src/lib/states/index.ts index 11166b2e3b..1d8fcc0d5d 100644 --- a/npm/ng-packs/packages/permission-management/src/lib/states/index.ts +++ b/npm/ng-packs/packages/permission-management/src/lib/states/index.ts @@ -1 +1 @@ -export * from "./permission-management.state"; +export * from './permission-management.state'; diff --git a/npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.ts b/npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.ts index a6040da45f..f9c90764c2 100644 --- a/npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.ts +++ b/npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.ts @@ -10,20 +10,20 @@ import { DeleteTenant, GetTenants, GetTenantById, - UpdateTenant, + UpdateTenant } from '../../actions/tenant-management.actions'; import { TenantManagementService } from '../../services/tenant-management.service'; import { TenantManagementState } from '../../states/tenant-management.state'; -type SelectedModalContent = { +interface SelectedModalContent { type: string; title: string; template: TemplateRef; -}; +} @Component({ selector: 'abp-tenants', - templateUrl: './tenants.component.html', + templateUrl: './tenants.component.html' }) export class TenantsComponent { @Select(TenantManagementState.get) @@ -44,21 +44,21 @@ export class TenantsComponent { selectedModalContent = {} as SelectedModalContent; - visibleFeatures: boolean = false; + visibleFeatures = false; providerKey: string; _useSharedDatabase: boolean; pageQuery: ABP.PageQueryParams = { - sorting: 'name', + sorting: 'name' }; - loading: boolean = false; + loading = false; - modalBusy: boolean = false; + modalBusy = false; - sortOrder: string = 'asc'; + sortOrder = 'asc'; get useSharedDatabase(): boolean { return this.defaultConnectionStringForm.get('useSharedDatabase').value; @@ -78,7 +78,7 @@ export class TenantsComponent { private confirmationService: ConfirmationService, private tenantService: TenantManagementService, private fb: FormBuilder, - private store: Store, + private store: Store ) {} onSearch(value) { @@ -88,14 +88,14 @@ export class TenantsComponent { private createTenantForm() { this.tenantForm = this.fb.group({ - name: [this.selected.name || '', [Validators.required, Validators.maxLength(256)]], + name: [this.selected.name || '', [Validators.required, Validators.maxLength(256)]] }); } private createDefaultConnectionStringForm() { this.defaultConnectionStringForm = this.fb.group({ useSharedDatabase: this._useSharedDatabase, - defaultConnectionString: [this.defaultConnectionString || ''], + defaultConnectionString: [this.defaultConnectionString || ''] }); } @@ -103,7 +103,7 @@ export class TenantsComponent { this.selectedModalContent = { title, template, - type, + type }; this.isModalVisible = true; @@ -117,7 +117,7 @@ export class TenantsComponent { switchMap(selected => { this.selected = selected; return this.tenantService.getDefaultConnectionString(id); - }), + }) ) .subscribe(fetchedConnectionString => { this._useSharedDatabase = fetchedConnectionString ? false : true; @@ -158,7 +158,7 @@ export class TenantsComponent { .deleteDefaultConnectionString(this.selected.id) .pipe( take(1), - finalize(() => (this.modalBusy = false)), + finalize(() => (this.modalBusy = false)) ) .subscribe(() => { this.isModalVisible = false; @@ -168,7 +168,7 @@ export class TenantsComponent { .updateDefaultConnectionString({ id: this.selected.id, defaultConnectionString: this.connectionString }) .pipe( take(1), - finalize(() => (this.modalBusy = false)), + finalize(() => (this.modalBusy = false)) ) .subscribe(() => { this.isModalVisible = false; @@ -184,9 +184,9 @@ export class TenantsComponent { .dispatch( this.selected.id ? new UpdateTenant({ ...this.tenantForm.value, id: this.selected.id }) - : new CreateTenant(this.tenantForm.value), + : new CreateTenant(this.tenantForm.value) ) - .pipe(finalize(()=> (this.modalBusy = false))) + .pipe(finalize(() => (this.modalBusy = false))) .subscribe(() => { this.isModalVisible = false; }); @@ -195,7 +195,7 @@ export class TenantsComponent { delete(id: string, name: string) { this.confirmationService .warn('AbpTenantManagement::TenantDeletionConfirmationMessage', 'AbpTenantManagement::AreYouSure', { - messageLocalizationParams: [name], + messageLocalizationParams: [name] }) .subscribe((status: Toaster.Status) => { if (status === Toaster.Status.confirm) { @@ -220,6 +220,6 @@ export class TenantsComponent { } changeSortOrder() { - this.sortOrder = this.sortOrder.toLowerCase() === "asc" ? "desc" : "asc"; + this.sortOrder = this.sortOrder.toLowerCase() === 'asc' ? 'desc' : 'asc'; } } diff --git a/npm/ng-packs/packages/tenant-management/src/lib/services/tenant-management.service.ts b/npm/ng-packs/packages/tenant-management/src/lib/services/tenant-management.service.ts index 5763efa4d2..bb043377e6 100644 --- a/npm/ng-packs/packages/tenant-management/src/lib/services/tenant-management.service.ts +++ b/npm/ng-packs/packages/tenant-management/src/lib/services/tenant-management.service.ts @@ -4,7 +4,7 @@ import { RestService, Rest, ABP } from '@abp/ng.core'; import { TenantManagement } from '../models/tenant-management'; @Injectable({ - providedIn: 'root', + providedIn: 'root' }) export class TenantManagementService { constructor(private rest: RestService) {} @@ -13,7 +13,7 @@ export class TenantManagementService { const request: Rest.Request = { method: 'GET', url: '/api/multi-tenancy/tenants', - params, + params }; return this.rest.request(request); @@ -22,7 +22,7 @@ export class TenantManagementService { getTenantById(id: string): Observable { const request: Rest.Request = { method: 'GET', - url: `/api/multi-tenancy/tenants/${id}`, + url: `/api/multi-tenancy/tenants/${id}` }; return this.rest.request(request); @@ -31,7 +31,7 @@ export class TenantManagementService { deleteTenant(id: string): Observable { const request: Rest.Request = { method: 'DELETE', - url: `/api/multi-tenancy/tenants/${id}`, + url: `/api/multi-tenancy/tenants/${id}` }; return this.rest.request(request); @@ -40,8 +40,8 @@ export class TenantManagementService { createTenant(body: TenantManagement.AddRequest): Observable { const request: Rest.Request = { method: 'POST', - url: `/api/multi-tenancy/tenants`, - body, + url: '/api/multi-tenancy/tenants', + body }; return this.rest.request(request); @@ -54,7 +54,7 @@ export class TenantManagementService { const request: Rest.Request = { method: 'PUT', url, - body, + body }; return this.rest.request(request); @@ -66,7 +66,7 @@ export class TenantManagementService { const request: Rest.Request = { method: 'GET', responseType: Rest.ResponseType.Text, - url, + url }; return this.rest.request(request); } @@ -77,7 +77,7 @@ export class TenantManagementService { const request: Rest.Request = { method: 'PUT', url, - params: { defaultConnectionString: payload.defaultConnectionString }, + params: { defaultConnectionString: payload.defaultConnectionString } }; return this.rest.request(request); } @@ -87,7 +87,7 @@ export class TenantManagementService { const request: Rest.Request = { method: 'DELETE', - url, + url }; return this.rest.request(request); } 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 c942aa7c92..6459515b23 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 @@ -6,7 +6,7 @@ import { eLayoutType, SetLanguage, SessionState, - takeUntilDestroy, + takeUntilDestroy } from '@abp/ng.core'; import { AfterViewInit, @@ -16,7 +16,7 @@ import { TemplateRef, TrackByFunction, ViewChild, - ViewChildren, + ViewChildren } from '@angular/core'; import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'; import { Navigate, RouterState } from '@ngxs/router-plugin'; @@ -32,7 +32,7 @@ import { LayoutState } from '../../states'; @Component({ selector: 'abp-layout-application', - templateUrl: './application-layout.component.html', + templateUrl: './application-layout.component.html' }) export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy { // required for dynamic component @@ -59,9 +59,9 @@ export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy { @ViewChildren('navbarRootDropdown', { read: NgbDropdown }) navbarRootDropdowns: QueryList; - isOpenChangePassword: boolean = false; + isOpenChangePassword = false; - isOpenProfile: boolean = false; + isOpenProfile = false; isDropdownChildDynamic: boolean; @@ -71,16 +71,13 @@ export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy { get defaultLanguage$(): Observable { return this.languages$.pipe( - map( - languages => snq(() => languages.find(lang => lang.cultureName === this.selectedLangCulture).displayName), - '', - ), + map(languages => snq(() => languages.find(lang => lang.cultureName === this.selectedLangCulture).displayName), '') ); } get dropdownLanguages$(): Observable { return this.languages$.pipe( - map(languages => snq(() => languages.filter(lang => lang.cultureName !== this.selectedLangCulture)), []), + map(languages => snq(() => languages.filter(lang => lang.cultureName !== this.selectedLangCulture)), []) ); } @@ -116,8 +113,8 @@ export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy { this.store.dispatch( new AddNavigationElement([ { element: this.languageRef, order: 4, name: 'LanguageRef' }, - { element: this.currentUserRef, order: 5, name: 'CurrentUserRef' }, - ]), + { element: this.currentUserRef, order: 5, name: 'CurrentUserRef' } + ]) ); } @@ -125,7 +122,7 @@ export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy { .pipe( map(elements => elements.map(({ element }) => element)), filter(elements => !compare(elements, this.rightPartElements)), - takeUntilDestroy(this), + takeUntilDestroy(this) ) .subscribe(elements => { setTimeout(() => (this.rightPartElements = elements), 0); @@ -136,7 +133,7 @@ export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy { fromEvent(window, 'resize') .pipe( takeUntilDestroy(this), - debounceTime(250), + debounceTime(250) ) .subscribe(() => { this.checkWindowWidth(); @@ -153,8 +150,8 @@ export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy { this.oauthService.logOut(); this.store.dispatch( new Navigate(['/'], null, { - state: { redirectUrl: this.store.selectSnapshot(RouterState).state.url }, - }), + state: { redirectUrl: this.store.selectSnapshot(RouterState).state.url } + }) ); this.store.dispatch(new GetAppConfiguration()); } diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/layout/layout.component.ts b/npm/ng-packs/packages/theme-basic/src/lib/components/layout/layout.component.ts index dce489a331..5566335671 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/layout/layout.component.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/layout/layout.component.ts @@ -6,10 +6,10 @@ import { Store } from '@ngxs/store'; @Component({ selector: ' abp-layout', templateUrl: './layout.component.html', - animations: [slideFromBottom], + animations: [slideFromBottom] }) export class LayoutComponent { - isCollapsed: boolean = true; + isCollapsed = true; get appInfo(): Config.Application { return this.store.selectSnapshot(ConfigState.getApplicationInfo); diff --git a/npm/ng-packs/packages/theme-basic/src/lib/constants/styles.ts b/npm/ng-packs/packages/theme-basic/src/lib/constants/styles.ts index 5b4e7f483a..3943859e84 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/constants/styles.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/constants/styles.ts @@ -6,4 +6,4 @@ export default ` .entry-row { margin-bottom: 15px; } -`; \ No newline at end of file +`; diff --git a/npm/ng-packs/packages/theme-basic/src/lib/states/index.ts b/npm/ng-packs/packages/theme-basic/src/lib/states/index.ts index 71bff26b8b..1e257fc64c 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/states/index.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/states/index.ts @@ -1 +1 @@ -export * from "./layout.state"; +export * from './layout.state'; diff --git a/npm/ng-packs/packages/theme-basic/src/lib/theme-basic.module.ts b/npm/ng-packs/packages/theme-basic/src/lib/theme-basic.module.ts index 25cb7d0ebd..8d7a50df65 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/theme-basic.module.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/theme-basic.module.ts @@ -28,19 +28,19 @@ export const LAYOUTS = [ApplicationLayoutComponent, AccountLayoutComponent, Empt NgxValidateCoreModule.forRoot({ targetSelector: '.form-group', blueprints: { - email: `AbpAccount::ThisFieldIsNotAValidEmailAddress.`, - max: `AbpAccount::ThisFieldMustBeBetween{0}And{1}[{{ min }},{{ max }}]`, - maxlength: `AbpAccount::ThisFieldMustBeAStringWithAMaximumLengthOf{1}[{{ requiredLength }}]`, - min: `AbpAccount::ThisFieldMustBeBetween{0}And{1}[{{ min }},{{ max }}]`, - minlength: `AbpAccount::ThisFieldMustBeAStringOrArrayTypeWithAMinimumLengthOf[{{ min }},{{ max }}]`, - required: `AbpAccount::ThisFieldIsRequired.`, - passwordMismatch: `AbpIdentity::Identity.PasswordConfirmationFailed`, + email: 'AbpAccount::ThisFieldIsNotAValidEmailAddress.', + max: 'AbpAccount::ThisFieldMustBeBetween{0}And{1}[{{ min }},{{ max }}]', + maxlength: 'AbpAccount::ThisFieldMustBeAStringWithAMaximumLengthOf{1}[{{ requiredLength }}]', + min: 'AbpAccount::ThisFieldMustBeBetween{0}And{1}[{{ min }},{{ max }}]', + minlength: 'AbpAccount::ThisFieldMustBeAStringOrArrayTypeWithAMinimumLengthOf[{{ min }},{{ max }}]', + required: 'AbpAccount::ThisFieldIsRequired.', + passwordMismatch: 'AbpIdentity::Identity.PasswordConfirmationFailed' }, - errorTemplate: ValidationErrorComponent, - }), + errorTemplate: ValidationErrorComponent + }) ], exports: [...LAYOUTS], - entryComponents: [...LAYOUTS, ValidationErrorComponent], + entryComponents: [...LAYOUTS, ValidationErrorComponent] }) export class ThemeBasicModule { constructor(private initialService: InitialService) {} diff --git a/npm/ng-packs/packages/theme-shared/package.json b/npm/ng-packs/packages/theme-shared/package.json index 690ed974b0..818febc4a3 100644 --- a/npm/ng-packs/packages/theme-shared/package.json +++ b/npm/ng-packs/packages/theme-shared/package.json @@ -10,8 +10,8 @@ "chart.js": "^2.8.0", "font-awesome": "^4.7.0", "ngx-perfect-scrollbar": "^8.0.0", - "primeicons": "^1.0.0", - "primeng": "^8.0.0" + "primeicons": "^2.0.0", + "primeng": "^8.0.3" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/theme-shared/src/lib/abstracts/toaster.ts b/npm/ng-packs/packages/theme-shared/src/lib/abstracts/toaster.ts index 5dbba869f1..c911be1e43 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/abstracts/toaster.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/abstracts/toaster.ts @@ -2,12 +2,12 @@ import { MessageService } from 'primeng/components/common/messageservice'; import { Observable, Subject } from 'rxjs'; import { Toaster } from '../models/toaster'; -export class AbstractToaster { +export abstract class AbstractToaster { status$: Subject; - key: string = 'abpToast'; + key = 'abpToast'; - sticky: boolean = false; + sticky = false; constructor(protected messageService: MessageService) {} @@ -36,7 +36,7 @@ export class AbstractToaster { summary: title || '', ...options, key: this.key, - ...(typeof (options || ({} as any)).sticky === 'undefined' && { sticky: this.sticky }), + ...(typeof (options || ({} as any)).sticky === 'undefined' && { sticky: this.sticky }) }); this.status$ = new Subject(); return this.status$; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/animations/collapse.animations.ts b/npm/ng-packs/packages/theme-shared/src/lib/animations/collapse.animations.ts index ecbec49e99..af71b948df 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/animations/collapse.animations.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/animations/collapse.animations.ts @@ -5,15 +5,15 @@ export const collapse = trigger('collapse', [ 'open', style({ height: '*', - overflow: 'hidden', - }), + overflow: 'hidden' + }) ), state( 'close', style({ height: '0px', - overflow: 'hidden', - }), + overflow: 'hidden' + }) ), - transition(`open <=> close`, animate('{{duration}}ms'), { params: { duration: '350' } }), + transition('open <=> close', animate('{{duration}}ms'), { params: { duration: '350' } }) ]); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.html b/npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.html new file mode 100644 index 0000000000..fa8f7df3da --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.html @@ -0,0 +1,13 @@ + diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.ts index 09e8e588a5..1b0d4a5a4c 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.ts @@ -5,21 +5,7 @@ import { ConfigState, ABP } from '@abp/ng.core'; @Component({ selector: 'abp-breadcrumb', - template: ` - - `, + templateUrl: './breadcrumb.component.html' }) export class BreadcrumbComponent implements OnInit { show: boolean; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/button/button.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/button/button.component.ts index 7a915d667c..50dab84c0f 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/button/button.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/button/button.component.ts @@ -6,23 +6,23 @@ import { Component, Input } from '@angular/core'; - `, + ` }) export class ButtonComponent { @Input() - buttonClass: string = 'btn btn-primary'; + buttonClass = 'btn btn-primary'; @Input() - type: string = 'button'; + type = 'button'; @Input() iconClass: string; @Input() - loading: boolean = false; + loading = false; @Input() - disabled: boolean = false; + disabled = false; get icon(): string { return `${this.loading ? 'fa fa-pulse fa-spinner' : this.iconClass || 'd-none'}`; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/change-password/change-password.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/change-password/change-password.component.ts index 1f7dcab4af..f030e61e9c 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/change-password/change-password.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/change-password/change-password.component.ts @@ -8,7 +8,7 @@ import { Output, SimpleChanges, TemplateRef, - ViewChild, + ViewChild } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { comparePasswords, Validation } from '@ngx-validate/core'; @@ -23,7 +23,7 @@ const PASSWORD_FIELDS = ['newPassword', 'repeatNewPassword']; @Component({ selector: 'abp-change-password', - templateUrl: './change-password.component.html', + templateUrl: './change-password.component.html' }) export class ChangePasswordComponent implements OnInit, OnChanges { protected _visible; @@ -38,17 +38,16 @@ export class ChangePasswordComponent implements OnInit, OnChanges { this.visibleChange.emit(value); } - @Output() - visibleChange = new EventEmitter(); + @Output() readonly visibleChange = new EventEmitter(); @ViewChild('modalContent', { static: false }) modalContent: TemplateRef; form: FormGroup; - modalBusy: boolean = false; + modalBusy = false; - mapErrorsFn: Validation.MapErrorsFn = function(errors, groupErrors, control) { + mapErrorsFn: Validation.MapErrorsFn = (errors, groupErrors, control) => { if (PASSWORD_FIELDS.indexOf(control.name) < 0) return errors; return errors.concat(groupErrors.filter(({ key }) => key === 'passwordMismatch')); @@ -61,11 +60,11 @@ export class ChangePasswordComponent implements OnInit, OnChanges { { password: ['', required], newPassword: ['', required], - repeatNewPassword: ['', required], + repeatNewPassword: ['', required] }, { - validators: [comparePasswords(PASSWORD_FIELDS)], - }, + validators: [comparePasswords(PASSWORD_FIELDS)] + } ); } @@ -77,13 +76,13 @@ export class ChangePasswordComponent implements OnInit, OnChanges { .dispatch( new ChangePassword({ currentPassword: this.form.get('password').value, - newPassword: this.form.get('newPassword').value, - }), + newPassword: this.form.get('newPassword').value + }) ) .pipe( finalize(() => { this.modalBusy = false; - }), + }) ) .subscribe({ next: () => { @@ -92,9 +91,9 @@ export class ChangePasswordComponent implements OnInit, OnChanges { }, error: err => { this.toasterService.error(snq(() => err.error.error.message, 'AbpAccount::DefaultErrorMessage'), 'Error', { - life: 7000, + life: 7000 }); - }, + } }); } diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/chart/chart.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/chart/chart.component.ts index a3ff6bbdaa..45829c8ee4 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/chart/chart.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/chart/chart.component.ts @@ -6,7 +6,7 @@ import { Input, OnDestroy, Output, - ChangeDetectorRef, + ChangeDetectorRef } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { chartJsLoaded$ } from '../../utils/widget-utils'; @@ -14,7 +14,7 @@ declare const Chart: any; @Component({ selector: 'abp-chart', - templateUrl: './chart.component.html', + templateUrl: './chart.component.html' }) export class ChartComponent implements AfterViewInit, OnDestroy { @Input() type: string; @@ -27,11 +27,12 @@ export class ChartComponent implements AfterViewInit, OnDestroy { @Input() height: string; - @Input() responsive: boolean = true; + @Input() responsive = true; - @Output() onDataSelect: EventEmitter = new EventEmitter(); + // tslint:disable-next-line: no-output-on-prefix + @Output() readonly onDataSelect: EventEmitter = new EventEmitter(); - @Output() initialized = new BehaviorSubject(this); + @Output() readonly initialized = new BehaviorSubject(this); private _initialized: boolean; @@ -61,6 +62,7 @@ export class ChartComponent implements AfterViewInit, OnDestroy { ngAfterViewInit() { chartJsLoaded$.subscribe(() => { try { + // tslint:disable-next-line: no-unused-expression Chart; } catch (error) { console.error(`Chart is not found. Import the Chart from app.module like shown below: @@ -76,16 +78,20 @@ export class ChartComponent implements AfterViewInit, OnDestroy { onCanvasClick = event => { if (this.chart) { - let element = this.chart.getElementAtEvent(event); - let dataset = this.chart.getDatasetAtEvent(event); + const element = this.chart.getElementAtEvent(event); + const dataset = this.chart.getDatasetAtEvent(event); if (element && element[0] && dataset) { - this.onDataSelect.emit({ originalEvent: event, element: element[0], dataset: dataset }); + this.onDataSelect.emit({ + originalEvent: event, + element: element[0], + dataset + }); } } }; initChart = () => { - let opts = this.options || {}; + const opts = this.options || {}; opts.responsive = this.responsive; // allows chart to resize in responsive mode @@ -97,7 +103,7 @@ export class ChartComponent implements AfterViewInit, OnDestroy { type: this.type, data: this.data, options: this.options, - plugins: this.plugins, + plugins: this.plugins }); this.cdRef.detectChanges(); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/confirmation/confirmation.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/confirmation/confirmation.component.ts index 39cf9b73a2..a1911b38ff 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/confirmation/confirmation.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/confirmation/confirmation.component.ts @@ -4,6 +4,7 @@ import { Toaster } from '../../models/toaster'; @Component({ selector: 'abp-confirmation', + // tslint:disable-next-line: component-max-inline-declarations template: ` diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/errors/error.component.scss b/npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.scss similarity index 100% rename from npm/ng-packs/packages/theme-shared/src/lib/components/errors/error.component.scss rename to npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.scss diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.ts new file mode 100644 index 0000000000..824fe683a7 --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.ts @@ -0,0 +1,22 @@ +import { Component, Renderer2, ElementRef } from '@angular/core'; + +@Component({ + selector: 'abp-error', + templateUrl: './error.component.html', + styleUrls: ['error.component.scss'] +}) +export class ErrorComponent { + title = 'Oops!'; + + details = 'Sorry, an error has occured.'; + + renderer: Renderer2; + + elementRef: ElementRef; + + host: any; + + destroy() { + this.renderer.removeChild(this.host, this.elementRef.nativeElement); + } +} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/errors/error.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/errors/error.component.ts deleted file mode 100644 index 708b48dc47..0000000000 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/errors/error.component.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Component, Renderer2, ElementRef } from '@angular/core'; - -@Component({ - selector: 'abp-error', - template: ` -
- -
-
-
-

- {{ title | abpLocalization }} -

-
- {{ details | abpLocalization }} -
- -
-
-
-
- `, - styleUrls: ['error.component.scss'], -}) -export class ErrorComponent { - title = 'Oops!'; - - details = 'Sorry, an error has occured.'; - - renderer: Renderer2; - - elementRef: ElementRef; - - host: any; - - destroy() { - this.renderer.removeChild(this.host, this.elementRef.nativeElement); - } -} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts index da797cc844..d81550edaa 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts @@ -20,27 +20,9 @@ import { filter } from 'rxjs/operators'; > `, - styleUrls: ['./loader-bar.component.scss'], + styleUrls: ['./loader-bar.component.scss'] }) export class LoaderBarComponent implements OnDestroy { - @Input() - containerClass: string = 'abp-loader-bar'; - - @Input() - color: string = '#77b6ff'; - - @Input() - isLoading: boolean = false; - - @Input() - filter = (action: StartLoader | StopLoader) => action.payload.url.indexOf('openid-configuration') < 0; - - progressLevel: number = 0; - - interval: Subscription; - - timer: Subscription; - get boxShadow(): string { return `0 0 10px rgba(${this.color}, 0.5)`; } @@ -50,7 +32,7 @@ export class LoaderBarComponent implements OnDestroy { .pipe( ofActionSuccessful(StartLoader, StopLoader), filter(this.filter), - takeUntilDestroy(this), + takeUntilDestroy(this) ) .subscribe(action => { if (action instanceof StartLoader) this.startLoading(); @@ -61,15 +43,32 @@ export class LoaderBarComponent implements OnDestroy { .pipe( filter( event => - event instanceof NavigationStart || event instanceof NavigationEnd || event instanceof NavigationError, + event instanceof NavigationStart || event instanceof NavigationEnd || event instanceof NavigationError ), - takeUntilDestroy(this), + takeUntilDestroy(this) ) .subscribe(event => { if (event instanceof NavigationStart) this.startLoading(); else this.stopLoading(); }); } + @Input() + containerClass = 'abp-loader-bar'; + + @Input() + color = '#77b6ff'; + + @Input() + isLoading = false; + + progressLevel = 0; + + interval: Subscription; + + timer: Subscription; + + @Input() + filter = (action: StartLoader | StopLoader) => action.payload.url.indexOf('openid-configuration') < 0; ngOnDestroy() { this.interval.unsubscribe(); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts index e38ba270cb..5e6ebff228 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts @@ -9,7 +9,7 @@ import { Renderer2, TemplateRef, ViewChild, - ViewChildren, + ViewChildren } from '@angular/core'; import { fromEvent, Subject, timer } from 'rxjs'; import { filter, take, takeUntil, debounceTime } from 'rxjs/operators'; @@ -23,7 +23,7 @@ const ANIMATION_TIMEOUT = 200; @Component({ selector: 'abp-modal', - templateUrl: './modal.component.html', + templateUrl: './modal.component.html' }) export class ModalComponent implements OnDestroy { @Input() @@ -51,7 +51,7 @@ export class ModalComponent implements OnDestroy { this.renderer.addClass(this.modalContent.nativeElement, 'fade-out-top'); setTimeout(() => { this.setVisible(value); - this.ngOnDestroy(); + this.destroy$.next(); }, ANIMATION_TIMEOUT - 10); } } @@ -68,9 +68,9 @@ export class ModalComponent implements OnDestroy { this._busy = value; } - @Input() centered: boolean = false; + @Input() centered = false; - @Input() modalClass: string = ''; + @Input() modalClass = ''; @Input() size: ModalSize = 'lg'; @@ -78,9 +78,9 @@ export class ModalComponent implements OnDestroy { @Input() minHeight: number; - @Output() visibleChange = new EventEmitter(); + @Output() readonly visibleChange = new EventEmitter(); - @Output() init = new EventEmitter(); + @Output() readonly init = new EventEmitter(); @ContentChild('abpHeader', { static: false }) abpHeader: TemplateRef; @@ -88,29 +88,29 @@ export class ModalComponent implements OnDestroy { @ContentChild('abpFooter', { static: false }) abpFooter: TemplateRef; - @ContentChild('abpClose', { static: false, read: ElementRef }) abpClose: ElementRef; + @ContentChild('abpClose', { static: false, read: ElementRef }) + abpClose: ElementRef; - @ContentChild(ButtonComponent, { static: false, read: ButtonComponent }) abpSubmit: ButtonComponent; + @ContentChild(ButtonComponent, { static: false, read: ButtonComponent }) + abpSubmit: ButtonComponent; @ViewChild('abpModalContent', { static: false }) modalContent: ElementRef; @ViewChildren('abp-button') abpButtons; - @Output() - show = new EventEmitter(); + @Output() readonly appear = new EventEmitter(); - @Output() - hide = new EventEmitter(); + @Output() readonly disappear = new EventEmitter(); - _visible: boolean = false; + _visible = false; - _busy: boolean = false; + _busy = false; - showModal: boolean = false; + showModal = false; - isOpenConfirmation: boolean = false; + isOpenConfirmation = false; - closable: boolean = false; + closable = false; destroy$ = new Subject(); @@ -131,11 +131,11 @@ export class ModalComponent implements OnDestroy { .subscribe(_ => (this.closable = true)); this.renderer.addClass(document.body, 'modal-open'); - this.show.emit(); + this.appear.emit(); } else { this.closable = false; this.renderer.removeClass(document.body, 'modal-open'); - this.hide.emit(); + this.disappear.emit(); } } @@ -144,7 +144,7 @@ export class ModalComponent implements OnDestroy { .pipe( takeUntil(this.destroy$), debounceTime(150), - filter((key: KeyboardEvent) => key && key.code === 'Escape' && this.closable), + filter((key: KeyboardEvent) => key && key.code === 'Escape' && this.closable) ) .subscribe(_ => { this.close(); @@ -155,7 +155,7 @@ export class ModalComponent implements OnDestroy { fromEvent(this.abpClose.nativeElement, 'click') .pipe( takeUntil(this.destroy$), - filter(() => !!(this.closable && this.modalContent)), + filter(() => !!(this.closable && this.modalContent)) ) .subscribe(() => this.close()); }, 0); @@ -167,7 +167,7 @@ export class ModalComponent implements OnDestroy { if (!this.closable || this.busy) return; const nodes = getFlatNodes( - (this.modalContent.nativeElement.querySelector('#abp-modal-body') as HTMLElement).childNodes, + (this.modalContent.nativeElement.querySelector('#abp-modal-body') as HTMLElement).childNodes ); if (hasNgDirty(nodes)) { @@ -194,7 +194,7 @@ export class ModalComponent implements OnDestroy { function getFlatNodes(nodes: NodeList): HTMLElement[] { return Array.from(nodes).reduce( (acc, val) => [...acc, ...(val.childNodes && val.childNodes.length ? getFlatNodes(val.childNodes) : [val])], - [], + [] ); } diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/profile/profile.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/profile/profile.component.ts index 0dc8ef2768..e7d110365b 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/profile/profile.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/profile/profile.component.ts @@ -9,7 +9,7 @@ const { maxLength, required, email } = Validators; @Component({ selector: 'abp-profile', - templateUrl: './profile.component.html', + templateUrl: './profile.component.html' }) export class ProfileComponent implements OnChanges { protected _visible; @@ -24,15 +24,14 @@ export class ProfileComponent implements OnChanges { this.visibleChange.emit(value); } - @Output() - visibleChange = new EventEmitter(); + @Output() readonly visibleChange = new EventEmitter(); @Select(ProfileState.getProfile) profile$: Observable; form: FormGroup; - modalBusy: boolean = false; + modalBusy = false; constructor(private fb: FormBuilder, private store: Store) {} @@ -41,7 +40,7 @@ export class ProfileComponent implements OnChanges { .dispatch(new GetProfile()) .pipe( withLatestFrom(this.profile$), - take(1), + take(1) ) .subscribe(([, profile]) => { this.form = this.fb.group({ @@ -49,7 +48,7 @@ export class ProfileComponent implements OnChanges { email: [profile.email, [required, email, maxLength(256)]], name: [profile.name || '', [maxLength(64)]], surname: [profile.surname || '', [maxLength(64)]], - phoneNumber: [profile.phoneNumber || '', [maxLength(16)]], + phoneNumber: [profile.phoneNumber || '', [maxLength(16)]] }); }); } diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/table-empty-message/table-empty-message.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/table-empty-message/table-empty-message.component.ts index e53b90129e..5924a9f187 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/table-empty-message/table-empty-message.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/table-empty-message/table-empty-message.component.ts @@ -1,12 +1,13 @@ import { Component, OnInit, Input } from '@angular/core'; @Component({ + // tslint:disable-next-line: component-selector selector: '[abp-table-empty-message]', template: ` {{ emptyMessage | abpLocalization }} - `, + ` }) export class TableEmptyMessageComponent { @Input() @@ -16,10 +17,10 @@ export class TableEmptyMessageComponent { message: string; @Input() - localizationResource: string = 'AbpAccount'; + localizationResource = 'AbpAccount'; @Input() - localizationProp: string = 'NoDataAvailableInDatatable'; + localizationProp = 'NoDataAvailableInDatatable'; get emptyMessage(): string { return this.message || `${this.localizationResource}::${this.localizationProp}`; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/toast/toast.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/toast/toast.component.ts index 382b18974b..422c67f3b9 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/toast/toast.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/toast/toast.component.ts @@ -2,6 +2,7 @@ import { Component } from '@angular/core'; @Component({ selector: 'abp-toast', + // tslint:disable-next-line: component-max-inline-declarations template: ` @@ -20,6 +21,6 @@ import { Component } from '@angular/core'; - `, + ` }) export class ToastComponent {} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/contants/scripts.ts b/npm/ng-packs/packages/theme-shared/src/lib/contants/scripts.ts index 1bc3ad0fa9..08d725cd4e 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/contants/scripts.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/contants/scripts.ts @@ -1 +1 @@ -export default ``; +export default ''; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts b/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts index 0baff0da8f..d2b9bdb63f 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts @@ -11,31 +11,36 @@ import { import { Navigate, RouterState } from '@ngxs/router-plugin'; import { Actions, ofActionSuccessful, Store } from '@ngxs/store'; import { Observable } from 'rxjs'; -import { ErrorComponent } from '../components/errors/error.component'; +import { ErrorComponent } from '../components/error/error.component'; import { Toaster } from '../models/toaster'; import { ConfirmationService } from '../services/confirmation.service'; import snq from 'snq'; -const DEFAULTS = { +export const DEFAULT_ERROR_MESSAGES = { defaultError: { - message: 'An error has occurred!', + title: 'An error has occurred!', details: 'Error detail not sent by server.', }, - defaultError401: { - message: 'You are not authenticated!', + title: 'You are not authenticated!', details: 'You should be authenticated (sign in) in order to perform this operation.', }, - defaultError403: { - message: 'You are not authorized!', + title: 'You are not authorized!', details: 'You are not allowed to perform this operation.', }, - defaultError404: { - message: 'Resource not found!', + title: 'Resource not found!', details: 'The resource requested could not found on the server.', }, + defaultError500: { + title: '500', + details: 'AbpAccount::InternalServerErrorMessage', + }, + defaultErrorUnknown: { + title: 'Unknown Error', + details: 'AbpAccount::InternalServerErrorMessage', + }, }; @Injectable({ providedIn: 'root' }) @@ -51,7 +56,7 @@ export class ErrorHandler { ) { actions.pipe(ofActionSuccessful(RestOccurError)).subscribe(res => { const { payload: err = {} as HttpErrorResponse | any } = res; - const body = snq(() => (err as HttpErrorResponse).error.error, DEFAULTS.defaultError.message); + const body = snq(() => (err as HttpErrorResponse).error.error, DEFAULT_ERROR_MESSAGES.defaultError.title); if (err instanceof HttpErrorResponse && err.headers.get('_AbpErrorFormat')) { const confirmation$ = this.showError(null, null, body); @@ -64,35 +69,39 @@ export class ErrorHandler { } else { switch ((err as HttpErrorResponse).status) { case 401: - this.showError(DEFAULTS.defaultError401.details, DEFAULTS.defaultError401.message).subscribe(() => - this.navigateToLogin(), - ); + this.showError( + DEFAULT_ERROR_MESSAGES.defaultError401.details, + DEFAULT_ERROR_MESSAGES.defaultError401.title, + ).subscribe(() => this.navigateToLogin()); break; case 403: this.createErrorComponent({ - title: DEFAULTS.defaultError403.message, - details: DEFAULTS.defaultError403.details, + title: DEFAULT_ERROR_MESSAGES.defaultError403.title, + details: DEFAULT_ERROR_MESSAGES.defaultError403.details, }); break; case 404: - this.showError(DEFAULTS.defaultError404.details, DEFAULTS.defaultError404.message); + this.showError( + DEFAULT_ERROR_MESSAGES.defaultError404.details, + DEFAULT_ERROR_MESSAGES.defaultError404.title, + ); break; case 500: this.createErrorComponent({ - title: '500', - details: 'AbpAccount::InternalServerErrorMessage', + title: DEFAULT_ERROR_MESSAGES.defaultError500.title, + details: DEFAULT_ERROR_MESSAGES.defaultError500.details, }); break; case 0: if ((err as HttpErrorResponse).statusText === 'Unknown Error') { this.createErrorComponent({ - title: 'Unknown Error', - details: 'AbpAccount::InternalServerErrorMessage', + title: DEFAULT_ERROR_MESSAGES.defaultErrorUnknown.title, + details: DEFAULT_ERROR_MESSAGES.defaultErrorUnknown.details, }); } break; default: - this.showError(DEFAULTS.defaultError.details, DEFAULTS.defaultError.message); + this.showError(DEFAULT_ERROR_MESSAGES.defaultError.details, DEFAULT_ERROR_MESSAGES.defaultError.title); break; } } @@ -105,7 +114,7 @@ export class ErrorHandler { message = body.details; title = body.message; } else { - message = body.message || DEFAULTS.defaultError.message; + message = body.message || DEFAULT_ERROR_MESSAGES.defaultError.title; } } @@ -125,7 +134,7 @@ export class ErrorHandler { createErrorComponent(instance: Partial) { const renderer = this.rendererFactory.createRenderer(null, null); - const host = renderer.selectRootElement('app-root', true); + const host = renderer.selectRootElement(document.body, true); const componentRef = this.cfRes.resolveComponentFactory(ErrorComponent).create(this.injector); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/services/confirmation.service.ts b/npm/ng-packs/packages/theme-shared/src/lib/services/confirmation.service.ts index f8338d95e8..69bb5eaf72 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/services/confirmation.service.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/services/confirmation.service.ts @@ -8,9 +8,9 @@ import { Toaster } from '../models/toaster'; @Injectable({ providedIn: 'root' }) export class ConfirmationService extends AbstractToaster { - key: string = 'abpConfirmation'; + key = 'abpConfirmation'; - sticky: boolean = true; + sticky = true; destroy$ = new Subject(); @@ -22,7 +22,7 @@ export class ConfirmationService extends AbstractToaster { message: string, title: string, severity: Toaster.Severity, - options?: Confirmation.Options, + options?: Confirmation.Options ): Observable { this.listenToEscape(); @@ -40,7 +40,7 @@ export class ConfirmationService extends AbstractToaster { .pipe( takeUntil(this.destroy$), debounceTime(150), - filter((key: KeyboardEvent) => key && key.code === 'Escape'), + filter((key: KeyboardEvent) => key && key.code === 'Escape') ) .subscribe(_ => { this.clear(); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/button.component.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/button.component.spec.ts new file mode 100644 index 0000000000..b8682ae224 --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/button.component.spec.ts @@ -0,0 +1,54 @@ +import { CoreModule } from '@abp/ng.core'; +import { + createComponentFactory, + createHostFactory, + Spectator, + SpectatorHost, + createTestComponentFactory +} from '@ngneat/spectator'; +import { ButtonComponent } from '../components'; + +describe('ButtonComponent', () => { + let host: SpectatorHost; + + const createHost = createHostFactory(ButtonComponent); + + beforeEach(() => (host = createHost('Button'))); + + it('should display the button', () => { + expect(host.query('button')).toBeTruthy(); + }); + + it('should equal the default classes to btn btn-primary', () => { + expect(host.query('button')).toHaveClass('btn btn-primary'); + }); + + it('should equal the default type to button', () => { + expect(host.query('button')).toHaveAttribute('type', 'button'); + }); + + it('should enabled', () => { + expect(host.query('[disabled]')).toBeFalsy(); + }); + + it('should have the text content', () => { + expect(host.query('button')).toHaveText('Button'); + }); + + it('should display the icon', () => { + expect(host.query('i.d-none')).toBeFalsy(); + expect(host.query('i')).toHaveClass('fa'); + }); + + it('should display the spinner icon', () => { + host.component.loading = true; + host.detectComponentChanges(); + expect(host.query('i')).toHaveClass('fa-spinner'); + }); + + it('should disabled when the loading input is true', () => { + host.component.loading = true; + host.detectComponentChanges(); + expect(host.query('[disabled]')).toBeDefined(); + }); +}); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/error.handler.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/error.handler.spec.ts new file mode 100644 index 0000000000..26e3753609 --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/error.handler.spec.ts @@ -0,0 +1,109 @@ +import { CoreModule, RestOccurError, RouterOutletComponent } from '@abp/ng.core'; +import { Location } from '@angular/common'; +import { HttpErrorResponse, HttpHeaders } from '@angular/common/http'; +import { Component } from '@angular/core'; +import { createRoutingFactory, SpectatorRouting } from '@ngneat/spectator'; +import { RouterState } from '@ngxs/router-plugin'; +import { NgxsModule, Store } from '@ngxs/store'; +import { NgxsResetPluginModule, StateOverwrite } from 'ngxs-reset-plugin'; +import { DEFAULT_ERROR_MESSAGES, ErrorHandler } from '../handlers'; +import { ThemeSharedModule } from '../theme-shared.module'; + +@Component({ selector: 'abp-dummy', template: 'dummy works! ' }) +class DummyComponent { + constructor(public errorHandler: ErrorHandler, public store: Store) {} +} + +describe('With Custom Host Component', () => { + let component: SpectatorRouting; + const createComponent = createRoutingFactory({ + component: DummyComponent, + imports: [CoreModule, ThemeSharedModule.forRoot(), NgxsModule.forRoot([]), NgxsResetPluginModule.forRoot()], + stubsEnabled: false, + routes: [{ path: '', component: DummyComponent }, { path: 'account/login', component: RouterOutletComponent }] + }); + + beforeEach(() => { + component = createComponent(); + const abpError = document.querySelector('abp-error'); + if (abpError) document.body.removeChild(abpError); + }); + + it('should display the error component when server error occurs', () => { + component.component.store.dispatch(new RestOccurError(new HttpErrorResponse({ status: 500 }))); + component.detectChanges(); + expect(document.querySelector('.error-template')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError500.title); + expect(document.querySelector('.error-details')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError500.details); + }); + + it('should display the error component when authorize error occurs', () => { + component.component.store.dispatch(new RestOccurError(new HttpErrorResponse({ status: 403 }))); + component.detectChanges(); + expect(document.querySelector('.error-template')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError403.title); + expect(document.querySelector('.error-details')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError403.details); + }); + + it('should display the error component when unknown error occurs', () => { + component.component.store.dispatch( + new RestOccurError(new HttpErrorResponse({ status: 0, statusText: 'Unknown Error' })) + ); + component.detectChanges(); + expect(document.querySelector('.error-template')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultErrorUnknown.title); + expect(document.querySelector('.error-details')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultErrorUnknown.details); + }); + + it('should display the confirmation when not found error occurs', () => { + component.component.store.dispatch(new RestOccurError(new HttpErrorResponse({ status: 404 }))); + component.detectChanges(); + expect(component.query('.abp-confirm-summary')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError404.title); + expect(component.query('.abp-confirm-body')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError404.details); + }); + + it('should display the confirmation when default error occurs', () => { + component.component.store.dispatch(new RestOccurError(new HttpErrorResponse({ status: 412 }))); + component.detectChanges(); + expect(component.query('.abp-confirm-summary')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError.title); + expect(component.query('.abp-confirm-body')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError.details); + }); + + it('should display the confirmation when authenticated error occurs', async () => { + component.component.store.dispatch(new RestOccurError(new HttpErrorResponse({ status: 401 }))); + component.detectChanges(); + + component.component.store.dispatch(new StateOverwrite([RouterState, { state: { url: '/' } }])); + component.click('#confirm'); + await component.fixture.whenStable(); + expect(component.get(Location).path()).toBe('/account/login'); + }); + + it('should display the confirmation when authenticated error occurs with _AbpErrorFormat header', async () => { + let headers: HttpHeaders = new HttpHeaders(); + headers = headers.append('_AbpErrorFormat', '_AbpErrorFormat'); + + component.component.store.dispatch(new RestOccurError(new HttpErrorResponse({ status: 401, headers }))); + component.detectChanges(); + component.component.store.dispatch(new StateOverwrite([RouterState, { state: { url: '/' } }])); + component.click('#confirm'); + await component.fixture.whenStable(); + expect(component.get(Location).path()).toBe('/account/login'); + }); + + it('should display the confirmation when error occurs with _AbpErrorFormat header', () => { + let headers: HttpHeaders = new HttpHeaders(); + headers = headers.append('_AbpErrorFormat', '_AbpErrorFormat'); + + component.component.store.dispatch( + new RestOccurError( + new HttpErrorResponse({ + error: { error: { message: 'test message', details: 'test detail' } }, + status: 412, + headers + }) + ) + ); + component.detectChanges(); + + expect(component.query('.abp-confirm-summary')).toHaveText('test message'); + expect(component.query('.abp-confirm-body')).toHaveText('test detail'); + }); +}); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts b/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts index c6dddaa04f..a1839f6217 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts @@ -9,7 +9,7 @@ import { ButtonComponent } from './components/button/button.component'; import { ChangePasswordComponent } from './components/change-password/change-password.component'; import { ChartComponent } from './components/chart/chart.component'; import { ConfirmationComponent } from './components/confirmation/confirmation.component'; -import { ErrorComponent } from './components/errors/error.component'; +import { ErrorComponent } from './components/error/error.component'; import { LoaderBarComponent } from './components/loader-bar/loader-bar.component'; import { ModalComponent } from './components/modal/modal.component'; import { ProfileComponent } from './components/profile/profile.component'; @@ -21,7 +21,7 @@ import { TableEmptyMessageComponent } from './components/table-empty-message/tab import { NgxValidateCoreModule } from '@ngx-validate/core'; export function appendScript(injector: Injector) { - const fn = function() { + const fn = () => { import('chart.js').then(() => chartJsLoaded$.next(true)); const lazyLoadService: LazyLoadService = injector.get(LazyLoadService); @@ -32,8 +32,8 @@ export function appendScript(injector: Injector) { 'style', styles, 'head', - 'afterbegin', - ) /* lazyLoadService.load(null, 'script', scripts) */, + 'afterbegin' + ) /* lazyLoadService.load(null, 'script', scripts) */ ).pipe(take(1)); }; @@ -53,7 +53,7 @@ export function appendScript(injector: Injector) { ModalComponent, ProfileComponent, TableEmptyMessageComponent, - ToastComponent, + ToastComponent ], exports: [ BreadcrumbComponent, @@ -65,9 +65,9 @@ export function appendScript(injector: Injector) { ModalComponent, ProfileComponent, TableEmptyMessageComponent, - ToastComponent, + ToastComponent ], - entryComponents: [ErrorComponent], + entryComponents: [ErrorComponent] }) export class ThemeSharedModule { static forRoot(): ModuleWithProviders { @@ -78,10 +78,10 @@ export class ThemeSharedModule { provide: APP_INITIALIZER, multi: true, deps: [Injector, ErrorHandler], - useFactory: appendScript, + useFactory: appendScript }, - { provide: MessageService, useClass: MessageService }, - ], + { provide: MessageService, useClass: MessageService } + ] }; } } diff --git a/npm/ng-packs/packages/theme-shared/tslint.json b/npm/ng-packs/packages/theme-shared/tslint.json index 124133f849..9d39c7dc74 100644 --- a/npm/ng-packs/packages/theme-shared/tslint.json +++ b/npm/ng-packs/packages/theme-shared/tslint.json @@ -1,17 +1,7 @@ { "extends": "../../tslint.json", "rules": { - "directive-selector": [ - true, - "attribute", - "lib", - "camelCase" - ], - "component-selector": [ - true, - "element", - "lib", - "kebab-case" - ] + "directive-selector": [true, "attribute", "abp", "camelCase"], + "component-selector": [true, "element", "abp", "kebab-case"] } } diff --git a/npm/ng-packs/tslint.json b/npm/ng-packs/tslint.json index 0888c34adb..348320008c 100644 --- a/npm/ng-packs/tslint.json +++ b/npm/ng-packs/tslint.json @@ -1,55 +1,100 @@ { "extends": "tslint:recommended", + "rulesDirectory": ["node_modules/codelyzer"], "rules": { "array-type": false, + "contextual-lifecycle": true, + "component-class-suffix": [true, "Component"], + "directive-class-suffix": [true, "Directive"], + "max-line-length": [true, 140], + "no-consecutive-blank-lines": false, + "no-redundant-jsdoc": true, + "no-var-requires": false, + "object-literal-key-quotes": [true, "as-needed"], + "ordered-imports": false, + "trailing-comma": false, + "component-max-inline-declarations": [true, { "animations": 20, "styles": 10, "template": 10 }], + "no-forward-ref": true, + "no-lifecycle-call": true, + "no-pipe-impure": true, + "no-queries-metadata-property": true, + "no-unused-css": true, + "prefer-output-readonly": true, + "template-conditional-complexity": [true, 4], + "use-component-selector": true, + "max-classes-per-file": false, "arrow-parens": false, + "arrow-return-shorthand": true, + "callable-types": true, + "class-name": true, + "component-selector": [true, "element", "abp", "kebab-case"], + "curly": false, "deprecation": { "severity": "warn" }, - "component-class-suffix": true, - "contextual-lifecycle": true, - "directive-class-suffix": true, "directive-selector": [true, "attribute", "abp", "camelCase"], - "component-selector": [true, "element", "abp", "kebab-case"], + "forin": true, "import-blacklist": [true, "rxjs/Rx"], - "interface-name": false, - "max-classes-per-file": false, - "max-line-length": [true, 140], - "member-access": false, + "interface-over-type-literal": true, + "interface-name": [true, "never-prefix"], + "member-access": [true, "no-public"], "member-ordering": [ true, { "order": ["static-field", "instance-field", "static-method", "instance-method"] } ], - "no-consecutive-blank-lines": false, + "no-arg": true, + "no-bitwise": true, + "no-conflicting-lifecycle": true, "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], + "no-construct": true, + "no-debugger": true, + "no-duplicate-super": true, + "no-empty-interface": true, "no-empty": false, - "no-inferrable-types": [false, "ignore-params"], - "no-non-null-assertion": true, - "no-redundant-jsdoc": true, - "no-switch-case-fall-through": true, - "no-use-before-declare": true, - "no-var-requires": false, - "curly": false, - "object-literal-key-quotes": [true, "as-needed"], - "object-literal-sort-keys": false, - "ordered-imports": false, - "quotemark": [true, "single"], - "trailing-comma": false, - "no-conflicting-lifecycle": true, + "no-eval": true, "no-host-metadata-property": true, - "no-input-rename": true, + "no-inferrable-types": [true, "ignore-params"], + "no-input-rename": false, "no-inputs-metadata-property": true, + "no-misused-new": true, + "no-namespace": false, + "no-non-null-assertion": true, "no-output-native": true, "no-output-on-prefix": true, - "no-output-rename": true, + "no-output-rename": false, "no-outputs-metadata-property": true, - "no-namespace": false, - "template-banana-in-box": true, - "template-no-negated-async": true, + "no-shadowed-variable": true, + "no-string-literal": false, + "no-string-throw": true, + "no-switch-case-fall-through": true, + "no-unnecessary-initializer": true, + "no-unnecessary-semicolons": false, + "no-unused-expression": true, + "no-var-keyword": true, + "object-literal-sort-keys": false, + "prefer-const": true, + "quotemark": [true, "single", "avoid-escape", "avoid-template"], + "radix": true, + "semicolon": [true, "always", "ignore-bound-class-methods"], + // "template-accessibility-alt-text": true, + // "template-accessibility-elements-content": true, + // "template-accessibility-label-for": true, + // "template-accessibility-tabindex-no-positive": true, + // "template-accessibility-table-scope": true, + // "template-accessibility-valid-aria": true, + // "template-banana-in-box": true, + // "template-click-events-have-key-events": true, + // "template-mouse-events-have-key-events": true, + // "template-no-autofocus": true, + // "template-no-distracting-elements": true, + // "template-no-negated-async": true, + "triple-equals": [true, "allow-null-check"], + "unified-signatures": true, "use-lifecycle-interface": true, - "use-pipe-transform-interface": true - }, - "rulesDirectory": ["codelyzer"] + "use-pipe-transform-interface": true, + "variable-name": false, + "prefer-for-of": false + } }