diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/errors/error-500.component.scss b/npm/ng-packs/packages/theme-shared/src/lib/components/errors/error-500.component.scss
new file mode 100644
index 0000000000..a98b5a3ca6
--- /dev/null
+++ b/npm/ng-packs/packages/theme-shared/src/lib/components/errors/error-500.component.scss
@@ -0,0 +1,15 @@
+.error {
+ position: fixed;
+ top: 0;
+ background-color: #fff;
+ width: 100vw;
+ height: 100vh;
+ z-index: 999999;
+}
+
+.centered {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+}
diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/errors/error-500.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/errors/error-500.component.ts
new file mode 100644
index 0000000000..d4871f20ab
--- /dev/null
+++ b/npm/ng-packs/packages/theme-shared/src/lib/components/errors/error-500.component.ts
@@ -0,0 +1,33 @@
+import { Component, OnInit } from '@angular/core';
+import { Store } from '@ngxs/store';
+
+@Component({
+ selector: 'abp-error-500',
+ template: `
+
+
+
+
+
+ Oops!
+
+
+ Sorry, an error has occured.
+
+
+
+
+
+
+ `,
+ styleUrls: ['error-500.component.scss'],
+})
+export class Error500Component implements OnInit {
+ constructor(private store: Store) {}
+
+ ngOnInit(): void {}
+}
diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.scss b/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.scss
new file mode 100644
index 0000000000..a5031940a0
--- /dev/null
+++ b/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.scss
@@ -0,0 +1,23 @@
+.abp-loader-bar {
+ left: 0;
+ opacity: 0;
+ position: fixed;
+ top: 0;
+ transition: opacity 0.4s linear 0.4s;
+ z-index: 99999;
+
+ &.is-loading {
+ opacity: 1;
+ transition: none;
+ }
+
+ .abp-progress {
+ background: #77b6ff;
+ box-shadow: 0 0 10px rgba(119, 182, 255, 0.7);
+ height: 2px;
+ left: 0;
+ position: fixed;
+ top: 0;
+ transition: width 0.4s ease;
+ }
+}
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
new file mode 100644
index 0000000000..7ab7d2935e
--- /dev/null
+++ b/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts
@@ -0,0 +1,73 @@
+import { Component, OnInit, Input } from '@angular/core';
+import { Actions, ofActionSuccessful } from '@ngxs/store';
+import { LoaderStart, LoaderStop } from '@abp/ng.core';
+import { filter } from 'rxjs/operators';
+
+@Component({
+ selector: 'abp-loader-bar',
+ template: `
+
+ `,
+ styleUrls: ['./loader-bar.component.scss'],
+})
+export class LoaderBarComponent {
+ @Input()
+ containerClass: string = 'abp-loader-bar';
+
+ @Input()
+ progressClass: string = 'abp-progress';
+
+ @Input()
+ isLoading: boolean = false;
+
+ @Input()
+ filter = (action: LoaderStart | LoaderStop) => action.payload.url.indexOf('openid-configuration') < 0;
+
+ progressLevel: number = 0;
+
+ interval: any;
+
+ constructor(private actions: Actions) {
+ actions
+ .pipe(
+ ofActionSuccessful(LoaderStart, LoaderStop),
+ filter(this.filter),
+ )
+ .subscribe(action => {
+ if (action instanceof LoaderStart) {
+ this.startLoading();
+ } else {
+ this.stopLoading();
+ }
+ });
+ }
+
+ startLoading() {
+ this.isLoading = true;
+ const interval = setInterval(() => {
+ if (this.progressLevel < 75) {
+ this.progressLevel += Math.random() * 10;
+ } else if (this.progressLevel < 90) {
+ this.progressLevel += 0.4;
+ } else if (this.progressLevel < 100) {
+ this.progressLevel += 0.1;
+ } else {
+ clearInterval(interval);
+ }
+ }, 300);
+
+ this.interval = interval;
+ }
+
+ stopLoading() {
+ clearInterval(this.interval);
+ this.progressLevel = 100;
+ this.isLoading = false;
+
+ setTimeout(() => {
+ this.progressLevel = 0;
+ }, 800);
+ }
+}
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 fea98b1388..0d09158e23 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
@@ -1,11 +1,22 @@
import { RestOccurError } from '@abp/ng.core';
import { HttpErrorResponse } from '@angular/common/http';
-import { Injectable } from '@angular/core';
+import {
+ Injectable,
+ ApplicationRef,
+ ComponentFactoryResolver,
+ RendererFactory2,
+ Inject,
+ ReflectiveInjector,
+ Injector,
+ EmbeddedViewRef,
+} from '@angular/core';
import { Navigate, RouterState } from '@ngxs/router-plugin';
import { Actions, ofActionSuccessful, Store } from '@ngxs/store';
import { Observable } from 'rxjs';
import { Toaster } from '../models/toaster';
import { ConfirmationService } from '../services/confirmation.service';
+import { DOCUMENT } from '@angular/common';
+import { Error500Component } from '../components/errors/error-500.component';
const DEFAULTS = {
defaultError: {
@@ -31,7 +42,15 @@ const DEFAULTS = {
@Injectable({ providedIn: 'root' })
export class ErrorHandler {
- constructor(private actions: Actions, private store: Store, private confirmationService: ConfirmationService) {
+ constructor(
+ private actions: Actions,
+ private store: Store,
+ private confirmationService: ConfirmationService,
+ private appRef: ApplicationRef,
+ private cfRes: ComponentFactoryResolver,
+ private rendererFactory: RendererFactory2,
+ private injector: Injector,
+ ) {
actions.pipe(ofActionSuccessful(RestOccurError)).subscribe(res => {
const { payload: err = {} as HttpErrorResponse | any } = res;
const body = (err as HttpErrorResponse).error.error;
@@ -57,6 +76,14 @@ export class ErrorHandler {
case 404:
this.showError(DEFAULTS.defaultError404.details, DEFAULTS.defaultError404.message);
break;
+ case 500:
+ this.show500Component();
+ break;
+ case 0:
+ if ((err as HttpErrorResponse).statusText === 'Unknown Error') {
+ this.show500Component();
+ }
+ break;
default:
this.showError(DEFAULTS.defaultError.details, DEFAULTS.defaultError.message);
break;
@@ -88,4 +115,12 @@ export class ErrorHandler {
}),
);
}
+
+ private show500Component() {
+ const renderer = this.rendererFactory.createRenderer(null, null);
+ const host = renderer.selectRootElement('app-root', true);
+ const componentRef = this.cfRes.resolveComponentFactory(Error500Component).create(this.injector);
+ this.appRef.attachView(componentRef.hostView);
+ renderer.appendChild(host, (componentRef.hostView as EmbeddedViewRef).rootNodes[0]);
+ }
}
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 7f3fa40ef5..60ff43c8a3 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
@@ -11,6 +11,9 @@ import { ModalComponent } from './components/modal/modal.component';
import { ToastComponent } from './components/toast/toast.component';
import styles from './contants/styles';
import { ErrorHandler } from './handlers/error.handler';
+import { Error500Component } from './components/errors/error-500.component';
+import { LoaderBarComponent } from './components/loader-bar/loader-bar.component';
+import { Options } from './models/options';
export function appendScript(injector: Injector) {
const fn = function() {
@@ -39,11 +42,12 @@ export function appendScript(injector: Injector) {
targetSelector: '.form-group',
}),
],
- declarations: [ConfirmationComponent, ToastComponent, ModalComponent],
- exports: [NgbModalModule, ConfirmationComponent, ToastComponent, ModalComponent],
+ declarations: [ConfirmationComponent, ToastComponent, ModalComponent, Error500Component, LoaderBarComponent],
+ exports: [NgbModalModule, ConfirmationComponent, ToastComponent, ModalComponent, LoaderBarComponent],
+ entryComponents: [Error500Component],
})
export class ThemeSharedModule {
- static forRoot(): ModuleWithProviders {
+ static forRoot(options = {} as Options): ModuleWithProviders {
return {
ngModule: ThemeSharedModule,
providers: [