Added error handling and now possible to post employees

This commit is contained in:
Erik Tiekstra
2021-05-12 10:34:10 +02:00
parent eb18ea03a2
commit e21757b8e8
31 changed files with 27107 additions and 203 deletions

View File

@@ -14,3 +14,4 @@
<router-outlet></router-outlet>
</main>
</div>
<dafa-toast-list></dafa-toast-list>

View File

@@ -1,13 +1,15 @@
import { DigiNgNavigationBreadcrumbsModule } from '@af/digi-ng/_navigation/navigation-breadcrumbs';
import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { ErrorHandler, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule } from '@angular/router';
import { CustomErrorHandler } from '@dafa-interceptors/custom-error-handler.module';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { NavigationModule } from './components/navigation/navigation.module';
import { SidebarModule } from './components/sidebar/sidebar.module';
import { SkipToContentModule } from './components/skip-to-content/skip-to-content.module';
import { ToastListModule } from './components/toast-list/toast-list.module';
@NgModule({
declarations: [AppComponent],
@@ -19,9 +21,15 @@ import { SkipToContentModule } from './components/skip-to-content/skip-to-conten
SkipToContentModule,
NavigationModule,
SidebarModule,
ToastListModule,
DigiNgNavigationBreadcrumbsModule,
],
providers: [],
providers: [
{
provide: ErrorHandler,
useClass: CustomErrorHandler,
},
],
bootstrap: [AppComponent],
})
export class AppModule {}

View File

@@ -0,0 +1,15 @@
<section [ngClass]="className" *ngIf="errors$ | async as errors">
<ul class="toast-list__list" *ngIf="errors.length">
<li class="toast-list__item" *ngFor="let error of errors">
<dafa-toast [error]="error" (closeToast)="removeError($event)"></dafa-toast>
</li>
</ul>
<div class="dafa__a11y-sr-only" [attr.aria-live]="ariaLivePoliteness" [attr.aria-atomic]="ariaAtomic">
<ng-container *ngIf="errors.length">
<h3>{{ errors.length }} fel har uppstått!</h3>
<ul>
<li *ngFor="let error of errors">{{ error.name }}: {{ error.message }}</li>
</ul>
</ng-container>
</div>
</section>

View File

@@ -0,0 +1,55 @@
@import 'mixins/list';
.toast-list {
position: fixed;
pointer-events: none;
z-index: 9999;
margin: var(--digi--layout--gutter);
top: 0;
right: 0;
bottom: 0;
left: 0;
display: flex;
&--top-right {
justify-content: flex-end;
}
&--top-left {
justify-content: flex-start;
}
&--top-center {
justify-content: center;
}
&--center-right {
justify-content: flex-end;
align-items: center;
}
&--center-left {
justify-content: flex-start;
align-items: center;
}
&--center-center {
justify-content: center;
align-items: center;
}
&--bottom-right {
justify-content: flex-end;
align-items: flex-end;
}
&--bottom-left {
justify-content: flex-start;
align-items: flex-end;
}
&--bottom-center {
justify-content: center;
align-items: flex-end;
}
&__list {
@include dafa__reset-list;
}
&__item:not(:first-child) {
margin-top: var(--digi--layout--gutter);
}
}

View File

@@ -0,0 +1,25 @@
/* tslint:disable:no-unused-variable */
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { ToastListComponent } from './toast-list.component';
describe('ToastListComponent', () => {
let component: ToastListComponent;
let fixture: ComponentFixture<ToastListComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ToastListComponent],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ToastListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,30 @@
import { AriaLivePoliteness } from '@angular/cdk/a11y';
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { ToastPosition } from '@dafa-enums/toast-position.enum';
import { CustomError } from '@dafa-models/error/custom-error';
import { ErrorService } from '@dafa-services/error.service';
import { Observable } from 'rxjs';
@Component({
selector: 'dafa-toast-list',
templateUrl: './toast-list.component.html',
styleUrls: ['./toast-list.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ToastListComponent {
@Input() ariaLivePoliteness: AriaLivePoliteness = 'assertive';
@Input() ariaAtomic = true;
@Input() position: ToastPosition = ToastPosition.TOP_RIGHT;
errors$: Observable<CustomError[]> = this.errorService.errors$;
constructor(private errorService: ErrorService) {}
get className(): string {
return `toast-list toast-list--${this.position}`;
}
removeError(error: CustomError): void {
this.errorService.remove(error);
}
}

View File

@@ -0,0 +1,11 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { ToastListComponent } from './toast-list.component';
import { ToastModule } from './toast/toast.module';
@NgModule({
declarations: [ToastListComponent],
imports: [CommonModule, ToastModule],
exports: [ToastListComponent],
})
export class ToastListModule {}

View File

@@ -0,0 +1,14 @@
<div [ngClass]="className">
<div class="toast__icon-wrapper">
<digi-icon-exclamation-circle *ngIf="error.severity === errorSeverity.HIGH"></digi-icon-exclamation-circle>
<digi-icon-exclamation-triangle *ngIf="error.severity === errorSeverity.MEDIUM"></digi-icon-exclamation-triangle>
<digi-icon-check-circle-reg *ngIf="error.severity === errorSeverity.LOW"></digi-icon-check-circle-reg>
</div>
<div class="toast__content">
<button class="toast__close-button" aria-label="Stäng meddelandet" (click)="emitCloseEvent()">
<digi-icon-x></digi-icon-x>
</button>
<h3 class="toast__heading">{{ error.name }}</h3>
<p class="toast__message">{{ error.message }}</p>
</div>
</div>

View File

@@ -0,0 +1,63 @@
@import 'variables/shadows';
.toast {
position: relative;
display: flex;
align-items: stretch;
background-color: var(--digi--ui--color--informative);
border: 2px solid var(--digi--ui--color--informative);
box-shadow: $dafa__shadow;
pointer-events: auto;
color: var(--digi--typography--color--text);
font-size: 1rem;
&--high {
background-color: var(--digi--ui--color--danger);
border-color: var(--digi--ui--color--danger);
}
&--medium {
background-color: var(--digi--ui--color--warning);
border-color: var(--digi--ui--color--warning);
}
&__icon-wrapper {
display: flex;
align-items: center;
padding: var(--digi--layout--gutter);
color: var(--digi--typography--color--text--light);
font-size: 2rem;
.toast--medium & {
color: var(--digi--typography--color--text);
}
}
&__content {
display: flex;
background-color: var(--digi--ui--color--background);
flex-direction: column;
justify-content: center;
padding: var(--digi--layout--gutter--s);
}
&__heading {
margin: 0;
}
&__message {
max-width: 400px !important;
margin: 0;
overflow-wrap: break-word;
}
&__close-button {
background-color: transparent;
border-width: 0;
position: absolute;
top: 0;
right: 0;
padding: var(--digi--layout--gutter--s);
color: var(--digi--typography--color--text);
}
}

View File

@@ -0,0 +1,25 @@
/* tslint:disable:no-unused-variable */
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { ToastComponent } from './toast.component';
describe('ToastComponent', () => {
let component: ToastComponent;
let fixture: ComponentFixture<ToastComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ToastComponent],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ToastComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,32 @@
import { AfterViewInit, ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
import { ErrorSeverity } from '@dafa-enums/error-severity.enum';
import { CustomError } from '@dafa-models/error/custom-error';
@Component({
selector: 'dafa-toast',
templateUrl: './toast.component.html',
styleUrls: ['./toast.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ToastComponent implements AfterViewInit {
@Input() error: CustomError;
@Output() closeToast: EventEmitter<CustomError> = new EventEmitter();
errorSeverity = ErrorSeverity;
ngAfterViewInit(): void {
if (this.error.removeAfter) {
setTimeout(() => {
this.closeToast.emit(this.error);
}, this.error.removeAfter);
}
}
get className(): string {
return `toast toast--${this.error.severity.toLowerCase()}`;
}
emitCloseEvent(): void {
this.closeToast.emit(this.error);
}
}

View File

@@ -0,0 +1,21 @@
import { DigiNgIconExclamationCircleModule } from '@af/digi-ng/_icon/icon-exclamation-circle';
import { DigiNgIconExclamationTriangleModule } from '@af/digi-ng/_icon/icon-exclamation-triangle';
import { DigiNgIconInfoCircleRegModule } from '@af/digi-ng/_icon/icon-info-circle-reg';
import { DigiNgIconXModule } from '@af/digi-ng/_icon/icon-x';
import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { ToastComponent } from './toast.component';
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [ToastComponent],
imports: [
CommonModule,
DigiNgIconXModule,
DigiNgIconExclamationCircleModule,
DigiNgIconExclamationTriangleModule,
DigiNgIconInfoCircleRegModule,
],
exports: [ToastComponent],
})
export class ToastModule {}

View File

@@ -0,0 +1,5 @@
export enum ErrorSeverity {
HIGH = 'High',
MEDIUM = 'Medium',
LOW = 'Low'
}

View File

@@ -0,0 +1,6 @@
export enum ErrorType {
API = 'API Error',
NETWORK = 'Network Error',
APP = 'Application Error',
UNKNOWN = 'Unknown Error'
}

View File

@@ -0,0 +1,11 @@
export enum ToastPosition {
TOP_RIGHT = 'top-right',
TOP_CENTER = 'top-center',
TOP_LEFT = 'top-left',
BOTTOM_RIGHT = 'bottom-right',
BOTTOM_CENTER = 'bottom-center',
BOTTOM_LEFT = 'bottom-left',
CENTER_RIGHT = 'center-right',
CENTER_CENTER = 'center-center',
CENTER_LEFT = 'center-left',
}

View File

@@ -1,26 +0,0 @@
export interface EmployeesApiResponse {
pxMore: string;
pxObjClass: string;
pxPageCount: string;
pxQueryTimeStamp: string;
pxResultCount: string;
pxTotalResultCount: string;
pyMaxRecords: string;
pyObjClass: string;
pxResults: EmployeeResponse[];
pzPerformanceSettings: string[];
}
export interface EmployeeResponse {
pxInsHandle: string;
pxObjClass: string;
pyAccessGroup: string;
pyFirstName: string;
pyLastName: string;
pyOrganization: string;
pyOrgDivision: string;
pyOrgUnit: string;
pyTelephone: string;
pyUserIdentifier: string;
pyUserName: string;
}

View File

@@ -1,23 +0,0 @@
export interface UserResponse {
pxInsName: string;
pxLimitedAccess: string;
pxObjClass: string;
pyAccessGroup: string;
pyFirstName: string;
pyLastName: string;
pyLastSignon: string;
pyOrganization: string;
pyOrgDivision: string;
pyOrgUnit: string;
pyTelephone: string;
pyUserIdentifier: string;
pyUserName: string;
pyAccessGroupsAdditional: string[];
pyAddresses: {
Email: {
pxObjClass: string;
pxSubscript: string;
pyEmailAddress: string;
};
};
}

View File

@@ -1,27 +1,72 @@
import { mapPegaAccessGroupToAccessGroups, PegaAccessGroup } from '@dafa-enums/access-group.enum';
import { Agency } from '@dafa-models/agency.model';
import { EmployeeResponse } from './api/employee-response.model';
import { Participant } from './participant.model';
import { User } from './user.model';
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface Employee extends User {}
export interface EmployeeDetail extends Employee {
export interface Employee extends User {
languages: string[];
outOfOffice: {
start: Date;
end: Date;
}[];
authorisations: string[];
phone: string;
email: string;
ssn: string;
agencies: Agency[];
participants: Participant[];
}
export function mapEmployeeReponseToEmployee(data: EmployeeResponse): Employee {
export interface EmployeesApiResponse {
pxMore: string;
pxObjClass: string;
pxPageCount: string;
pxQueryTimeStamp: string;
pxResultCount: string;
pxTotalResultCount: string;
pyMaxRecords: string;
pyObjClass: string;
pxResults: EmployeeApiResponse[];
pzPerformanceSettings: string[];
}
export interface EmployeeApiResponse {
pxInsHandle: string;
pxObjClass: string;
pyAccessGroup: string;
pyFirstName: string;
pyLastName: string;
pyOrganization: string;
pyOrgDivision: string;
pyOrgUnit: string;
pyTelephone: string;
pyUserIdentifier: string;
pyUserName: string;
}
export interface EmployeeApiRequestData {
pyFirstName: string;
pyLastName: string;
pyTelephone: string;
}
export interface EmployeeApiPostResponse {
pxObjClass: string;
pyErrorMessage: string;
pyFirstName: string;
pyHasError: 'true' | 'false';
pyLastName: string;
pyTelephone: string;
pyUserIdentifier: string;
}
export function mapEmployeeToEmployeeApiRequestData(data: Employee): EmployeeApiRequestData {
return {
pyFirstName: data.firstName,
pyLastName: data.lastName,
pyTelephone: data.phone,
};
}
export function mapEmployeeReponseToEmployee(data: EmployeeApiResponse): Employee {
return {
id: data.pyUserIdentifier,
lastName: data.pyLastName,
@@ -35,5 +80,10 @@ export function mapEmployeeReponseToEmployee(data: EmployeeResponse): Employee {
accessGroups: mapPegaAccessGroupToAccessGroups(data.pyAccessGroup as PegaAccessGroup),
utforandeverksamhet: '',
service: '',
languages: [],
outOfOffice: null,
ssn: '',
agencies: [],
participants: [],
};
}

View File

@@ -0,0 +1,58 @@
import { ErrorSeverity } from '@dafa-enums/error-severity.enum';
import { ErrorType } from '@dafa-enums/error-type.enum';
export class CustomError implements Error {
id: string;
name: string;
message: string;
stack: string;
type: ErrorType;
severity: ErrorSeverity;
timestamp: Date;
error: Error;
removeAfter: number;
constructor(args: { error: Error; type: ErrorType; message?: string; severity?: ErrorSeverity; stack?: string }) {
this.timestamp = new Date();
this.id = this.timestamp.getTime().toString();
this.type = this.name = args.type;
this.message = args.message ? args.message : args.error.message;
this.severity = args.severity ? args.severity : ErrorSeverity.HIGH;
this.stack = args.stack ? args.stack : CustomError.getStack(args.error);
this.error = args.error;
this.removeAfter =
this.severity === ErrorSeverity.LOW ? 5000 : this.severity === ErrorSeverity.MEDIUM ? 10000 : 20000;
}
static getStack(error: Error): string {
if (!error) {
return '';
}
if (error.stack) {
return error.stack;
} else if ((error as any).error) {
return this.getStack((error as any).error);
} else {
return error as any;
}
}
}
export function errorToCustomError(error: Error): CustomError {
console.log(error);
const type = (error as any).type || ErrorType.UNKNOWN;
const message = error.message || (error as any);
const severity = ErrorSeverity.HIGH;
// this is done to avoid circular references while running in debug mode
if ((error as any).ngDebugContext) {
(error as any).ngDebugContext = {};
}
return new CustomError({
error,
type,
severity,
message,
});
}

View File

@@ -1,5 +1,4 @@
import { AccessGroup, mapPegaAccessGroupToAccessGroups, PegaAccessGroup } from '@dafa-enums/access-group.enum';
import { UserResponse } from './api/user-response.model';
export interface User {
id: string;
@@ -16,7 +15,31 @@ export interface User {
service: string;
}
export function mapUserReponseToUser(data: UserResponse): User {
export interface UserApiResponse {
pxInsName: string;
pxLimitedAccess: string;
pxObjClass: string;
pyAccessGroup: string;
pyFirstName: string;
pyLastName: string;
pyLastSignon: string;
pyOrganization: string;
pyOrgDivision: string;
pyOrgUnit: string;
pyTelephone: string;
pyUserIdentifier: string;
pyUserName: string;
pyAccessGroupsAdditional: string[];
pyAddresses: {
Email: {
pxObjClass: string;
pxSubscript: string;
pyEmailAddress: string;
};
};
}
export function mapUserApiReponseToUser(data: UserApiResponse): User {
return {
id: data.pyUserIdentifier,
lastName: data.pyLastName,

View File

@@ -0,0 +1,13 @@
import { ErrorHandler, Injectable } from '@angular/core';
import { CustomError, errorToCustomError } from '@dafa-models/error/custom-error';
import { ErrorService } from '@dafa-services/error.service';
@Injectable()
export class CustomErrorHandler implements ErrorHandler {
constructor(private errorService: ErrorService) {}
handleError(error: any): void {
const customError: CustomError = errorToCustomError(error);
this.errorService.add(customError);
}
}

View File

@@ -34,6 +34,14 @@
[afDisableValidStyle]="true"
[afInvalid]="ssnControl.invalid && ssnControl.dirty"
></digi-ng-form-input>
<digi-ng-form-input
class="create-account__input"
formControlName="phone"
afLabel="Telefonnummer"
[afInvalidMessage]="phoneControl.errors?.message || ''"
[afDisableValidStyle]="true"
[afInvalid]="phoneControl.invalid && phoneControl.dirty"
></digi-ng-form-input>
</div>
<div class="create-account__block">
<h2>Tjänst</h2>

View File

@@ -5,7 +5,6 @@ import { Router } from '@angular/router';
import { Service } from '@dafa-enums/service.enum';
import { EmployeeService } from '@dafa-services/api/employee.service';
import { RequiredValidator } from '@dafa-validators/required.validator';
import { SocialSecurityNumberValidator } from '@dafa-validators/social-security-number.validator';
import { BehaviorSubject } from 'rxjs';
@Component({
@@ -39,8 +38,11 @@ export class CreateAccountComponent {
this.formGroup = this.formBuilder.group({
firstName: this.formBuilder.control('', [RequiredValidator('Förnamn')]),
lastName: this.formBuilder.control('', [RequiredValidator('Efternamn')]),
ssn: this.formBuilder.control('', [RequiredValidator('Personnummer'), SocialSecurityNumberValidator()]),
employeeId: this.formBuilder.control('', [RequiredValidator('Personal-ID')]),
phone: this.formBuilder.control('', [RequiredValidator('Telefonnummer')]),
// ssn: this.formBuilder.control('', [RequiredValidator('Personnummer'), SocialSecurityNumberValidator()]),
// employeeId: this.formBuilder.control('', [RequiredValidator('Personal-ID')]),
ssn: this.formBuilder.control(''),
employeeId: this.formBuilder.control(''),
service: this.formBuilder.control(''),
permissions: this.formBuilder.control(false),
participant: this.formBuilder.control(false),
@@ -83,6 +85,9 @@ export class CreateAccountComponent {
get ssnControl(): AbstractControl {
return this.formGroup.get('ssn');
}
get phoneControl(): AbstractControl {
return this.formGroup.get('phone');
}
private _markFormAsDirty(): void {
Object.keys(this.formGroup.controls).forEach(control => {
@@ -101,7 +106,7 @@ export class CreateAccountComponent {
delete submittableValues.outOfOfficeStart;
delete submittableValues.outOfOfficeEnd;
const post = this.employeeService.createAccount(submittableValues).subscribe({
const post = this.employeeService.postNewEmployee(submittableValues).subscribe({
next: id => {
this.router.navigate(['/administration', 'personal', id]);
},

View File

@@ -1,69 +0,0 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { environment } from '@dafa-environment';
import { Employee, EmployeeDetail } from '@dafa-models/employee.model';
import { SortBy } from '@dafa-models/sort-by.model';
import { sort } from '@dafa-utils/sort.util';
import { BehaviorSubject, combineLatest, Observable, throwError } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
function filterEmployees(employees: Employee[], searchFilter: string): Employee[] {
return employees.filter(person => {
const searchValueExistsInName = person.fullName.toLowerCase().includes(searchFilter.toLowerCase());
return searchValueExistsInName;
});
}
@Injectable({
providedIn: 'root',
})
export class EmployeeService {
private _employeesApiUrl = `${environment.apiBase}/employees`;
private _allEmployees$: Observable<Employee[]> = this.httpClient.get<Employee[]>(this._employeesApiUrl, {
params: { _embed: 'participants' },
});
private _employeesSortBy$ = new BehaviorSubject<SortBy | null>({ key: 'fullName', reverse: false });
public employeesSortBy$: Observable<SortBy> = this._employeesSortBy$.asObservable();
private _searchFilter$ = new BehaviorSubject<string>('');
public searchFilter$: Observable<string> = this._searchFilter$.asObservable();
private _filteredEmployees$: Observable<Employee[]> = combineLatest([this._allEmployees$, this._searchFilter$]).pipe(
map(([employees, searchFilter]) => filterEmployees(employees, searchFilter))
);
public filteredEmployees$: Observable<Employee[]> = combineLatest([
this._filteredEmployees$,
this._employeesSortBy$,
]).pipe(
map(([employees, sortBy]) => {
return sortBy ? sort(employees, sortBy) : employees;
})
);
constructor(private httpClient: HttpClient) {}
public getDetailedEmployeeData(id: string): Observable<Employee> {
return this.httpClient.get<Employee>(`${this._employeesApiUrl}/${id}`, { params: { _embed: 'participants' } });
}
public setSearchFilter(value: string) {
this._searchFilter$.next(value);
}
public setEmployeesSortKey(key: keyof Employee) {
const currentSortBy = this._employeesSortBy$.getValue();
const reverse = currentSortBy?.key === key ? !currentSortBy.reverse : false;
this._employeesSortBy$.next({ key, reverse });
}
public createAccount(employeesData: EmployeeDetail): Observable<string> {
return this.httpClient.post<EmployeeDetail>(this._employeesApiUrl, employeesData).pipe(
map(data => data.id),
catchError(error => {
return throwError(error);
})
);
}
}

View File

@@ -1,8 +1,14 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { ErrorType } from '@dafa-enums/error-type.enum';
import { environment } from '@dafa-environment';
import { EmployeesApiResponse } from '@dafa-models/api/employee-response.model';
import { Employee, EmployeeDetail, mapEmployeeReponseToEmployee } from '@dafa-models/employee.model';
import {
Employee,
EmployeeApiPostResponse,
EmployeesApiResponse,
mapEmployeeReponseToEmployee,
mapEmployeeToEmployeeApiRequestData,
} from '@dafa-models/employee.model';
import { SortBy } from '@dafa-models/sort-by.model';
import { sort } from '@dafa-utils/sort.util';
import { BehaviorSubject, combineLatest, Observable, throwError } from 'rxjs';
@@ -64,12 +70,21 @@ export class EmployeeService {
this._employeesSortBy$.next({ key, reverse });
}
public createAccount(employeesData: EmployeeDetail): Observable<string> {
return this.httpClient.post<EmployeeDetail>(this._employeesApiUrl, employeesData).pipe(
map(data => data.id),
catchError(error => {
return throwError(error);
})
);
public postNewEmployee(employeeData: Employee): Observable<string> {
return this.httpClient
.post<EmployeeApiPostResponse>(
this._employeeApiUrl,
mapEmployeeToEmployeeApiRequestData(employeeData),
API_HEADERS
)
.pipe(
map(data => {
if (data.pyHasError === 'true') {
throw new Error(data.pyErrorMessage);
}
return data.pyUserIdentifier;
}),
catchError(error => throwError({ message: error, type: ErrorType.API }))
);
}
}

View File

@@ -1,8 +1,7 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { environment } from '@dafa-environment';
import { UserResponse } from '@dafa-models/api/user-response.model';
import { mapUserReponseToUser, User } from '@dafa-models/user.model';
import { mapUserApiReponseToUser, User, UserApiResponse } from '@dafa-models/user.model';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@@ -12,10 +11,10 @@ import { map } from 'rxjs/operators';
export class UserService {
private _userApiUrl = `${environment.api.default}/D_OperatorID`;
public currentUser$: Observable<User> = this.httpClient
.get<UserResponse>(this._userApiUrl, {
.get<UserApiResponse>(this._userApiUrl, {
headers: environment.api.headers,
})
.pipe(map(response => mapUserReponseToUser(response)));
.pipe(map(response => mapUserApiReponseToUser(response)));
constructor(private httpClient: HttpClient) {}
}

View File

@@ -0,0 +1,34 @@
import { ApplicationRef, Injectable, Injector } from '@angular/core';
import { CustomError } from '@dafa-models/error/custom-error';
import { BehaviorSubject, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable({
providedIn: 'root',
})
export class ErrorService {
private appRef: any;
private errorQueue$: BehaviorSubject<CustomError[]> = new BehaviorSubject([]);
public errors$: Observable<CustomError[]> = this.errorQueue$.pipe(
map(errors => errors.sort((a, b) => (a.timestamp > b.timestamp ? -1 : 1)))
);
constructor(private injector: Injector) {
// Workaround to fix change-detection when using Error interceptor
// See https://stackoverflow.com/a/37793791
setTimeout(() => (this.appRef = this.injector.get(ApplicationRef)));
}
public add(error: CustomError) {
console.error(error);
this.errorQueue$.next([...this.errorQueue$.value, error]);
this.appRef.tick();
}
public remove(error: CustomError) {
const newErrorQueue = this.errorQueue$.value.filter(currentError => currentError.id !== error.id);
this.errorQueue$.next(newErrorQueue);
this.appRef.tick();
}
}

View File

@@ -0,0 +1 @@
$dafa__shadow: 0 0.2rem 0.6rem 0 var(--digi--ui--color--shadow);

26604
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -45,6 +45,7 @@
"@af/auth": "^11.1.0",
"@af/digi-ng": "^14.0.0",
"@angular/animations": "^11.2.0",
"@angular/cdk": "^11.2.12",
"@angular/common": "^11.2.0",
"@angular/compiler": "^11.2.0",
"@angular/core": "^11.2.0",

View File

@@ -22,6 +22,7 @@
"@dafa-directives/*": ["apps/dafa-web/src/app/directives/*"],
"@dafa-enums/*": ["apps/dafa-web/src/app/data/enums/*"],
"@dafa-services/*": ["apps/dafa-web/src/app/services/*"],
"@dafa-interceptors/*": ["apps/dafa-web/src/app/interceptors/*"],
"@dafa-utils/*": ["apps/dafa-web/src/app/utils/*"],
"@dafa-validators/*": ["apps/dafa-web/src/app/utils/validators/*"],
"@dafa-environment": ["apps/dafa-web/src/environments/environment"]