diff --git a/client/src/app/app-routing.module.ts b/client/src/app/app-routing.module.ts index e9497a5c..773caf82 100644 --- a/client/src/app/app-routing.module.ts +++ b/client/src/app/app-routing.module.ts @@ -3,7 +3,7 @@ import {NgModule} from '@angular/core'; import {LoginComponent} from './login/login.component'; import {SetupComponent} from './setup/setup.component'; import {SetupGuardService} from './setup/setup-guard.service'; -import {KazukuAuthGuardService} from './common/auth/kazuku-auth-guard.service'; +import {KazukuAuthGuardService} from '@common/auth/kazuku-auth-guard.service'; import {IconNamesEnum} from "ngx-bootstrap-icons"; import {faGauge} from "@fortawesome/free-solid-svg-icons/faGauge"; import {faPlaneDeparture} from "@fortawesome/free-solid-svg-icons/faPlaneDeparture"; diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index 923dc368..815d9334 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts @@ -1,12 +1,10 @@ import {AfterViewInit, ChangeDetectorRef, Component, ElementRef, OnInit, Renderer2, ViewChild} from '@angular/core'; -import {AuthService} from './common/auth/auth.service'; +import {AuthService} from '@common/auth/auth.service'; import {Store} from '@ngrx/store'; import {SideBarComponent} from "./layout/side-bar/side-bar.component"; -import {AuthActions} from './common/auth/store'; +import {AuthActions} from '@common/auth/store'; import {tap} from 'rxjs/operators'; -// import {LoadAuth} from './common/auth/store/actions/auth.actions'; - @Component({ selector: 'kz-root', templateUrl: './app.component.html', diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts index 6070119a..7b17d5d5 100644 --- a/client/src/app/app.module.ts +++ b/client/src/app/app.module.ts @@ -11,18 +11,18 @@ import {SiteService} from './sites/site.service'; import {LoginComponent} from './login/login.component'; import {UserListComponent} from './users/user-list.component'; import {QueryService} from './queries/query.service'; -import {AuthService} from './common/auth/auth.service'; +import {AuthService} from '@common/auth/auth.service'; import {SetupComponent} from './setup/setup.component'; import {SetupService} from './setup/setup.service'; import {SetupGuardService} from './setup/setup-guard.service'; -import {KazukuAuthGuardService} from './common/auth/kazuku-auth-guard.service'; +import {KazukuAuthGuardService} from '@common/auth/kazuku-auth-guard.service'; import {CustomDataComponent} from './custom-data/custom-data.component'; import {CustomDataService} from './custom-data/custom-data.service'; -import {UnAuthenticatedResponseInterceptor} from './common/interceptors/unauthenticated-response.interceptor'; -import {HttpService} from './common/http.service'; +import {UnAuthenticatedResponseInterceptor} from '@common/interceptors/unauthenticated-response.interceptor'; +import {HttpService} from '@common/http.service'; import {ContextComponent} from './layout/context/context.component'; -import {AutofocusDirective} from './common/ui/autofocus.directive'; -import {BaseComponent} from './common/base-component'; +import {AutofocusDirective} from '@common/ui/autofocus.directive'; +import {BaseComponent} from '@common/base-component'; import {WorkspaceActionsComponent} from './layout/workspace-actions/workspace-actions.component'; import {DashboardModule} from './dashboard/dashboard.module'; import {OrganizationsModule} from './organizations/organizations.module'; @@ -36,16 +36,16 @@ import {environment} from '../environments/environment'; import {EffectsModule} from '@ngrx/effects'; import {RouterState, StoreRouterConnectingModule} from '@ngrx/router-store'; -import {IdbService} from './common/indexed-db/idb.service'; -import {AuthTokenCacheService} from './common/auth/auth-token-cache.service'; -import {AuthRequestInterceptor} from './common/interceptors/auth-request.interceptor'; -import {KazukuAuthProviderService} from './common/auth/kazuku-auth-provider.service'; +import {IdbService} from '@common/indexed-db/idb.service'; +import {AuthTokenCacheService} from '@common/auth/auth-token-cache.service'; +import {AuthRequestInterceptor} from '@common/interceptors/auth-request.interceptor'; +import {KazukuAuthProviderService} from '@common/auth/kazuku-auth-provider.service'; import {registerLocaleData} from '@angular/common'; import en from '@angular/common/locales/en'; import {FormsModule} from '@angular/forms'; import {SideBarComponent} from './layout/side-bar/side-bar.component'; import {appReducer, metaReducers} from './store/app.reducer'; -import {AuthEffects} from './common/auth/store/auth.effects'; +import {AuthEffects} from '@common/auth/store/auth.effects'; registerLocaleData(en); diff --git a/client/src/app/common/auth/kazuku-auth-provider.service.ts b/client/src/app/common/auth/kazuku-auth-provider.service.ts index c2529fc3..44691ab1 100644 --- a/client/src/app/common/auth/kazuku-auth-provider.service.ts +++ b/client/src/app/common/auth/kazuku-auth-provider.service.ts @@ -85,6 +85,6 @@ export class KazukuAuthProviderService implements IAuthProvider { private handleRequestTokenError(error) { console.error(error); - return observableThrowError(() => error || 'Server error'); + return observableThrowError(() => error ?? 'Server error'); } } diff --git a/client/src/app/common/generic.service.ts b/client/src/app/common/generic.service.ts index f3305201..ca25e806 100644 --- a/client/src/app/common/generic.service.ts +++ b/client/src/app/common/generic.service.ts @@ -2,7 +2,7 @@ import {throwError as observableThrowError, Observable} from 'rxjs'; import {Injectable} from '@angular/core'; import {environment} from '../../environments/environment'; import {BaseModel} from './base.model'; -import JsonUtils from './utils/json-utils'; +import JsonUtils from './utils/json.utils'; import {HttpService} from './http.service'; import {catchError, map} from 'rxjs/operators'; @@ -78,6 +78,6 @@ export class GenericService { handleError(error) { console.error(error); - return observableThrowError(error || 'Server error'); + return observableThrowError(() => error ?? 'Server error'); } } diff --git a/client/src/app/common/utils/json-utils.ts b/client/src/app/common/utils/json.utils.ts similarity index 100% rename from client/src/app/common/utils/json-utils.ts rename to client/src/app/common/utils/json.utils.ts diff --git a/client/src/app/common/utils/ngrx.utils.ts b/client/src/app/common/utils/ngrx.utils.ts new file mode 100644 index 00000000..74a35c61 --- /dev/null +++ b/client/src/app/common/utils/ngrx.utils.ts @@ -0,0 +1,17 @@ +// This class is kept simple for easier consumption. You must not add any dependencies that require injection here. If you +// need anything injected, you need to change this to a service and provide it in the app module, inject into constructors, etc, etc. +export default class NgRxUtils { + static getUpdateObjectFor(item) { + if (!item.id) { + console.error(`item must have id property in NgRxUtils.getUpdateObjectFor()`) + } + + const update = { + id: item.id, + changes: {...item} // changes must be serializable and not have a custom constructor + }; + + return update; + } + +} diff --git a/client/src/app/content/content-model.service.ts b/client/src/app/content/content-model.service.ts index 91502ad7..dfda4ef2 100644 --- a/client/src/app/content/content-model.service.ts +++ b/client/src/app/content/content-model.service.ts @@ -1,7 +1,7 @@ import {Inject, Injectable} from '@angular/core'; -import {GenericService} from '../common/generic.service'; +import {GenericService} from '@common/generic.service'; import {CustomSchema} from '../custom-schemas/custom-schema.model'; -import {HttpService} from '../common/http.service'; +import {HttpService} from '@common/http.service'; import {catchError, map} from 'rxjs/operators'; import {Observable} from 'rxjs'; diff --git a/client/src/app/content/content-models/create-model/create-model.component.ts b/client/src/app/content/content-models/create-model/create-model.component.ts index 9c7562c8..1a22356a 100644 --- a/client/src/app/content/content-models/create-model/create-model.component.ts +++ b/client/src/app/content/content-models/create-model/create-model.component.ts @@ -1,7 +1,7 @@ import {Component, OnInit} from '@angular/core'; import {FormArray, FormControl, FormGroup, Validators} from '@angular/forms'; import {CustomSchema} from '../../../custom-schemas/custom-schema.model'; -import {BaseComponent} from '../../../common/base-component'; +import {BaseComponent} from '@common/base-component'; import {ActivatedRoute, Router} from '@angular/router'; import {CustomSchemaService} from '../../../custom-schemas/custom-schema.service'; import {ContentModelService} from '../../content-model.service'; diff --git a/client/src/app/content/content-models/model-details/model-details.component.ts b/client/src/app/content/content-models/model-details/model-details.component.ts index d7541090..76546f2e 100644 --- a/client/src/app/content/content-models/model-details/model-details.component.ts +++ b/client/src/app/content/content-models/model-details/model-details.component.ts @@ -4,7 +4,7 @@ import {CustomSchema} from '../../../custom-schemas/custom-schema.model'; import {ActivatedRoute, Params} from '@angular/router'; import {ContentModelService} from '../../content-model.service'; import {share, switchMap, tap} from 'rxjs/operators'; -import {BaseComponent} from '../../../common/base-component'; +import {BaseComponent} from '@common/base-component'; import {FormArray, FormControl, FormGroup, Validators} from '@angular/forms'; @Component({ diff --git a/client/src/app/content/content-routing.module.ts b/client/src/app/content/content-routing.module.ts index 2865b058..c8f2ec67 100644 --- a/client/src/app/content/content-routing.module.ts +++ b/client/src/app/content/content-routing.module.ts @@ -1,7 +1,7 @@ import {NgModule} from '@angular/core'; import {RouterModule, Routes} from '@angular/router'; import {ContentDashboardComponent} from './container/content-dashboard/content-dashboard.component'; -import {KazukuAuthGuardService} from "../common/auth/kazuku-auth-guard.service"; +import {KazukuAuthGuardService} from "@common/auth/kazuku-auth-guard.service"; import {CustomDataComponent} from "../custom-data/custom-data.component"; diff --git a/client/src/app/content/content.service.ts b/client/src/app/content/content.service.ts index bd012e71..02f1afab 100644 --- a/client/src/app/content/content.service.ts +++ b/client/src/app/content/content.service.ts @@ -1,6 +1,6 @@ import {Inject, Injectable} from '@angular/core'; -import {GenericService} from '../common/generic.service'; -import {HttpService} from '../common/http.service'; +import {GenericService} from '@common/generic.service'; +import {HttpService} from '@common/http.service'; import {catchError, map} from 'rxjs/operators'; import {Observable} from 'rxjs'; diff --git a/client/src/app/custom-data/custom-data.component.ts b/client/src/app/custom-data/custom-data.component.ts index 7f1b04eb..708eb144 100644 --- a/client/src/app/custom-data/custom-data.component.ts +++ b/client/src/app/custom-data/custom-data.component.ts @@ -1,6 +1,6 @@ import {Component, OnInit} from '@angular/core'; import {CustomSchema} from '../custom-schemas/custom-schema.model'; -import {BaseComponent} from '../common/base-component'; +import {BaseComponent} from '@common/base-component'; import {CustomDataService} from './custom-data.service'; import {ActivatedRoute, Params, Router} from '@angular/router'; import {CustomSchemaService} from '../custom-schemas/custom-schema.service'; diff --git a/client/src/app/custom-data/custom-data.service.ts b/client/src/app/custom-data/custom-data.service.ts index 7a398823..d86f84b6 100644 --- a/client/src/app/custom-data/custom-data.service.ts +++ b/client/src/app/custom-data/custom-data.service.ts @@ -3,8 +3,8 @@ import {environment} from '../../environments/environment'; import {Observable} from 'rxjs'; -import {GenericService} from '../common/generic.service'; -import {HttpService} from '../common/http.service'; +import {GenericService} from '@common/generic.service'; +import {HttpService} from '@common/http.service'; import {catchError, map} from 'rxjs/operators'; @Injectable() diff --git a/client/src/app/custom-schemas/custom-schema.component.ts b/client/src/app/custom-schemas/custom-schema.component.ts index 8d8b6709..738dd94f 100644 --- a/client/src/app/custom-schemas/custom-schema.component.ts +++ b/client/src/app/custom-schemas/custom-schema.component.ts @@ -1,6 +1,6 @@ import {Component, OnInit, OnDestroy} from '@angular/core'; import {ActivatedRoute, Params, Router} from '@angular/router'; -import {BaseComponent} from '../common/base-component'; +import {BaseComponent} from '@common/base-component'; import {CustomSchema} from './custom-schema.model'; import {CustomSchemaService} from './custom-schema.service'; import {NgForm, FormArray, FormGroup, FormControl, Validators, AbstractControl} from '@angular/forms'; diff --git a/client/src/app/custom-schemas/custom-schema.service.ts b/client/src/app/custom-schemas/custom-schema.service.ts index 9ccb14f6..79371f91 100644 --- a/client/src/app/custom-schemas/custom-schema.service.ts +++ b/client/src/app/custom-schemas/custom-schema.service.ts @@ -1,7 +1,7 @@ import {Injectable, Inject} from '@angular/core'; import {Observable} from 'rxjs'; -import {GenericService} from '../common/generic.service'; -import {HttpService} from '../common/http.service'; +import {GenericService} from '@common/generic.service'; +import {HttpService} from '@common/http.service'; import {catchError, map} from 'rxjs/operators'; import {CustomSchema} from './custom-schema.model'; diff --git a/client/src/app/custom-schemas/field-builder.component.ts b/client/src/app/custom-schemas/field-builder.component.ts index fcd3e8b0..43da8226 100644 --- a/client/src/app/custom-schemas/field-builder.component.ts +++ b/client/src/app/custom-schemas/field-builder.component.ts @@ -1,5 +1,5 @@ import {Component, OnInit, Input, Output, EventEmitter} from '@angular/core'; -import {BaseComponent} from '../common/base-component'; +import {BaseComponent} from '@common/base-component'; import {FormGroup} from '@angular/forms'; diff --git a/client/src/app/custom-schemas/model-fields/model-field/field-settings/field-settings.component.ts b/client/src/app/custom-schemas/model-fields/model-field/field-settings/field-settings.component.ts index f818f7a7..998f9176 100644 --- a/client/src/app/custom-schemas/model-fields/model-field/field-settings/field-settings.component.ts +++ b/client/src/app/custom-schemas/model-fields/model-field/field-settings/field-settings.component.ts @@ -1,6 +1,6 @@ import {AfterViewInit, Component, OnInit, ViewChild} from '@angular/core'; -import {BaseComponent} from '../../../../common/base-component'; -import {FieldTypes} from '../../../../common/constants/field-types.constants'; +import {BaseComponent} from '@common/base-component'; +import {FieldTypes} from '@common/constants/field-types.constants'; import {BasicFieldSettingsComponent} from './basic-field-settings/basic-field-settings.component'; import {FormGroup} from '@angular/forms'; import {TabsetComponent} from 'ngx-bootstrap/tabs'; diff --git a/client/src/app/custom-schemas/model-fields/model-field/model-field.component.ts b/client/src/app/custom-schemas/model-fields/model-field/model-field.component.ts index e6a0fbeb..172cb1e6 100644 --- a/client/src/app/custom-schemas/model-fields/model-field/model-field.component.ts +++ b/client/src/app/custom-schemas/model-fields/model-field/model-field.component.ts @@ -1,6 +1,6 @@ import {Component, Input, OnInit} from '@angular/core'; -import {FieldTypes, FieldTypesUI} from '../../../common/constants/field-types.constants'; -import {BaseComponent} from '../../../common/base-component'; +import {FieldTypes, FieldTypesUI} from '@common/constants/field-types.constants'; +import {BaseComponent} from '@common/base-component'; import {FieldSettingsComponent} from './field-settings/field-settings.component'; import {FormControl, FormGroup} from '@angular/forms'; import {BsModalRef, BsModalService} from 'ngx-bootstrap/modal'; diff --git a/client/src/app/custom-schemas/model-fields/model-fields.component.ts b/client/src/app/custom-schemas/model-fields/model-fields.component.ts index f6afe5ed..cb4794d2 100644 --- a/client/src/app/custom-schemas/model-fields/model-fields.component.ts +++ b/client/src/app/custom-schemas/model-fields/model-fields.component.ts @@ -1,7 +1,7 @@ import {Component, Input, OnChanges, OnInit, SimpleChanges} from '@angular/core'; -import {BaseComponent} from '../../common/base-component'; +import {BaseComponent} from '@common/base-component'; import {FormGroup} from '@angular/forms'; -import {FieldTypes} from '../../common/constants/field-types.constants'; +import {FieldTypes} from '@common/constants/field-types.constants'; @Component({ selector: 'kz-model-fields', diff --git a/client/src/app/custom-schemas/schema-routing.module.ts b/client/src/app/custom-schemas/schema-routing.module.ts index 73e56020..9a39862c 100644 --- a/client/src/app/custom-schemas/schema-routing.module.ts +++ b/client/src/app/custom-schemas/schema-routing.module.ts @@ -1,7 +1,7 @@ import {NgModule} from '@angular/core'; import {RouterModule, Routes} from '@angular/router'; import {CustomSchemaListComponent} from './custom-schema-list.component'; -import {KazukuAuthGuardService} from '../common/auth/kazuku-auth-guard.service'; +import {KazukuAuthGuardService} from '@common/auth/kazuku-auth-guard.service'; import {CustomSchemaComponent} from './custom-schema.component'; const routes: Routes = [ diff --git a/client/src/app/layout/context/context.component.ts b/client/src/app/layout/context/context.component.ts index a776a319..3c0fb6d0 100644 --- a/client/src/app/layout/context/context.component.ts +++ b/client/src/app/layout/context/context.component.ts @@ -1,11 +1,11 @@ import {Component, OnInit} from '@angular/core'; -import {AuthService} from '../../common/auth/auth.service'; -import {BaseComponent} from '../../common/base-component'; -import {IUserContext} from '../../common/auth/user-context.model'; +import {AuthService} from '@common/auth/auth.service'; +import {BaseComponent} from '@common/base-component'; +import {IUserContext} from '@common/auth/user-context.model'; import {map, takeUntil, tap} from 'rxjs/operators'; import {faAtom} from '@fortawesome/free-solid-svg-icons'; import {Observable} from 'rxjs'; -import {AuthSelectors} from '../../common/auth/store'; +import {AuthSelectors} from '@common/auth/store'; import {Store} from '@ngrx/store'; @Component({ diff --git a/client/src/app/layout/nav-bar/nav-bar.component.ts b/client/src/app/layout/nav-bar/nav-bar.component.ts index 1a5725e8..29ab2c8c 100644 --- a/client/src/app/layout/nav-bar/nav-bar.component.ts +++ b/client/src/app/layout/nav-bar/nav-bar.component.ts @@ -1,10 +1,10 @@ import {Component, OnInit, OnDestroy, Input} from '@angular/core'; import {Router} from '@angular/router'; import {Observable, Subject} from 'rxjs'; -import {IUserContext} from '../../common/auth/user-context.model'; +import {IUserContext} from '@common/auth/user-context.model'; import {filter, takeUntil, tap} from 'rxjs/operators'; import {Store} from '@ngrx/store'; -import {AuthActions, AuthSelectors} from '../../common/auth/store'; +import {AuthActions, AuthSelectors} from '@common/auth/store'; @Component({ selector: 'kz-nav-bar', diff --git a/client/src/app/layout/workspace-actions/workspace-action-items.ts b/client/src/app/layout/workspace-actions/workspace-action-items.ts index eaf78ce5..1491e913 100644 --- a/client/src/app/layout/workspace-actions/workspace-action-items.ts +++ b/client/src/app/layout/workspace-actions/workspace-action-items.ts @@ -1,4 +1,4 @@ -import {WorkspaceActionItem} from '../../common/models/workspace-actions.model'; +import {WorkspaceActionItem} from '@common/models/workspace-actions.model'; import {faCoffee} from '@fortawesome/free-solid-svg-icons'; import {faSignOutAlt} from '@fortawesome/free-solid-svg-icons'; import {faUser} from '@fortawesome/free-solid-svg-icons'; diff --git a/client/src/app/layout/workspace-actions/workspace-actions.component.ts b/client/src/app/layout/workspace-actions/workspace-actions.component.ts index 8faeafdd..69c872d0 100644 --- a/client/src/app/layout/workspace-actions/workspace-actions.component.ts +++ b/client/src/app/layout/workspace-actions/workspace-actions.component.ts @@ -1,9 +1,9 @@ import {Component, OnInit} from '@angular/core'; import {Observable} from 'rxjs'; -import {WorkspaceActionItem} from '../../common/models/workspace-actions.model'; +import {WorkspaceActionItem} from '@common/models/workspace-actions.model'; import {WorkspaceActionsService} from './workspace-actions.service'; -import {IUserContext} from '../../common/auth/user-context.model'; -import {AuthActions, AuthSelectors} from '../../common/auth/store'; +import {IUserContext} from '@common/auth/user-context.model'; +import {AuthActions, AuthSelectors} from '@common/auth/store'; import {Store} from '@ngrx/store'; @Component({ diff --git a/client/src/app/layout/workspace-actions/workspace-actions.service.ts b/client/src/app/layout/workspace-actions/workspace-actions.service.ts index 52b0b593..8490a392 100644 --- a/client/src/app/layout/workspace-actions/workspace-actions.service.ts +++ b/client/src/app/layout/workspace-actions/workspace-actions.service.ts @@ -1,6 +1,6 @@ import {Injectable} from '@angular/core'; import {Observable, of} from 'rxjs'; -import {WorkspaceActionItem} from '../../common/models/workspace-actions.model'; +import {WorkspaceActionItem} from '@common/models/workspace-actions.model'; import {WorkspaceActionItems} from './workspace-action-items'; @Injectable({ diff --git a/client/src/app/login/login.component.ts b/client/src/app/login/login.component.ts index ce355d7c..ae926725 100644 --- a/client/src/app/login/login.component.ts +++ b/client/src/app/login/login.component.ts @@ -1,8 +1,9 @@ import {Component, OnDestroy, OnInit} from '@angular/core'; -import {AuthService} from '../common/auth/auth.service'; -import {User} from '../common/auth/user.model'; +import {AuthService} from '@common/auth/auth.service'; +import {User} from '@common/auth/user.model'; import {ActivatedRoute, Router} from '@angular/router'; +import * as AuthActions from '@common/auth/store/auth.actions'; import * as AuthActions from '../common/auth/store/auth.actions'; import {FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms'; import {Store} from '@ngrx/store'; diff --git a/client/src/app/organizations/organization-list/organization-list.component.ts b/client/src/app/organizations/organization-list/organization-list.component.ts index aa612317..56fd659d 100644 --- a/client/src/app/organizations/organization-list/organization-list.component.ts +++ b/client/src/app/organizations/organization-list/organization-list.component.ts @@ -1,13 +1,13 @@ import {Component, OnInit} from '@angular/core'; -import {Organization} from '../../common/auth/organization.model'; +import {Organization} from '@common/auth/organization.model'; import {OrganizationService} from '../shared/organization.service'; import {Router} from '@angular/router'; -import {AuthService} from '../../common/auth/auth.service'; -import {BaseComponent} from '../../common/base-component'; -import {IUserContext} from '../../common/auth/user-context.model'; +import {AuthService} from '@common/auth/auth.service'; +import {BaseComponent} from '@common/base-component'; +import {IUserContext} from '@common/auth/user-context.model'; import {startWith, takeUntil} from 'rxjs/operators'; import {Observable} from 'rxjs'; -import {AuthActions, AuthSelectors} from '../../common/auth/store'; +import {AuthActions, AuthSelectors} from '@common/auth/store'; import {Store} from '@ngrx/store'; @Component({ diff --git a/client/src/app/organizations/organization/organization.component.ts b/client/src/app/organizations/organization/organization.component.ts index 20591774..c34fac4e 100644 --- a/client/src/app/organizations/organization/organization.component.ts +++ b/client/src/app/organizations/organization/organization.component.ts @@ -1,11 +1,11 @@ import {Component, OnInit, OnDestroy} from '@angular/core'; import {ActivatedRoute, ParamMap, Params, Router} from '@angular/router'; -import {BaseComponent} from '../../common/base-component'; -import {Organization} from '../../common/auth/organization.model'; +import {BaseComponent} from '@common/base-component'; +import {Organization} from '@common/auth/organization.model'; import {OrganizationService} from '../shared/organization.service'; import {FormControl, FormGroup, NgForm, Validators} from '@angular/forms'; import {iif, Observable, of} from 'rxjs'; -import {filter, flatMap, mergeMap, share, switchMap, takeUntil, tap} from 'rxjs/operators'; +import {filter, mergeMap, share, switchMap, takeUntil, tap} from 'rxjs/operators'; @Component({ selector: 'kz-organization', diff --git a/client/src/app/organizations/organizations-routing.module.ts b/client/src/app/organizations/organizations-routing.module.ts index b161d57b..8de52db4 100644 --- a/client/src/app/organizations/organizations-routing.module.ts +++ b/client/src/app/organizations/organizations-routing.module.ts @@ -2,7 +2,7 @@ import {NgModule} from '@angular/core'; import {RouterModule, Routes} from '@angular/router'; import {OrganizationListComponent} from './organization-list/organization-list.component'; import {OrganizationComponent} from './organization/organization.component'; -import {KazukuAuthGuardService} from '../common/auth/kazuku-auth-guard.service'; +import {KazukuAuthGuardService} from '@common/auth/kazuku-auth-guard.service'; const routes: Routes = [ { diff --git a/client/src/app/organizations/shared/organization.service.ts b/client/src/app/organizations/shared/organization.service.ts index 7d08ca7d..f9f210ed 100644 --- a/client/src/app/organizations/shared/organization.service.ts +++ b/client/src/app/organizations/shared/organization.service.ts @@ -1,9 +1,9 @@ import {Injectable, Inject} from '@angular/core'; -import {Organization} from '../../common/auth/organization.model'; +import {Organization} from '@common/auth/organization.model'; import {catchError, map} from 'rxjs/operators'; -import {GenericService} from '../../common/generic.service'; -import {HttpService} from '../../common/http.service'; +import {GenericService} from '@common/generic.service'; +import {HttpService} from '@common/http.service'; @Injectable() export class OrganizationService extends GenericService { diff --git a/client/src/app/pages/page.component.ts b/client/src/app/pages/page.component.ts index 063b1ac9..249fe92e 100644 --- a/client/src/app/pages/page.component.ts +++ b/client/src/app/pages/page.component.ts @@ -3,7 +3,7 @@ import {ActivatedRoute, Params, Router} from '@angular/router'; import {NgForm} from '@angular/forms'; import {Template} from '../templates/template.model'; import {TemplateService} from '../templates/template.service'; -import {BaseComponent} from '../common/base-component'; +import {BaseComponent} from '@common/base-component'; import {Observable} from 'rxjs'; import {Site} from '../sites/site.model'; diff --git a/client/src/app/queries/queries-routing.module.ts b/client/src/app/queries/queries-routing.module.ts index 3f43fe0d..5c945b41 100644 --- a/client/src/app/queries/queries-routing.module.ts +++ b/client/src/app/queries/queries-routing.module.ts @@ -1,7 +1,7 @@ import {NgModule} from '@angular/core'; import {RouterModule, Routes} from '@angular/router'; import {QueriesComponent} from './queries.component'; -import {KazukuAuthGuardService} from "../common/auth/kazuku-auth-guard.service"; +import {KazukuAuthGuardService} from "@common/auth/kazuku-auth-guard.service"; import {QueryComponent} from "./query-details/query.component"; const routes: Routes = [ diff --git a/client/src/app/queries/query-details/query.component.ts b/client/src/app/queries/query-details/query.component.ts index 151f2569..b82b67dd 100644 --- a/client/src/app/queries/query-details/query.component.ts +++ b/client/src/app/queries/query-details/query.component.ts @@ -4,8 +4,8 @@ import {NgForm} from '@angular/forms'; import {of} from 'rxjs'; import * as _ from 'lodash-es'; -import {flatMap, takeUntil} from 'rxjs/operators'; -import {BaseComponent} from "../../common/base-component"; +import {switchMap, takeUntil} from 'rxjs/operators'; +import {BaseComponent} from "@common/base-component"; import {QueryService} from "../query.service"; import {Query} from "../query.model"; @@ -28,7 +28,7 @@ export class QueryComponent extends BaseComponent implements OnInit { ngOnInit() { this.route.params .pipe( - flatMap((params: Params) => { + switchMap((params: Params) => { const nameId = params['nameId'] || ''; if (nameId) { this.queryNameId = nameId; diff --git a/client/src/app/queries/query.service.ts b/client/src/app/queries/query.service.ts index ad303ff0..16b1a221 100644 --- a/client/src/app/queries/query.service.ts +++ b/client/src/app/queries/query.service.ts @@ -3,8 +3,8 @@ import {environment} from '../../environments/environment'; import {Query} from './query.model'; -import {GenericService} from '../common/generic.service'; -import {HttpService} from '../common/http.service'; +import {GenericService} from '@common/generic.service'; +import {HttpService} from '@common/http.service'; import {catchError, map} from 'rxjs/operators'; @Injectable() diff --git a/client/src/app/schedules/schedule.component.ts b/client/src/app/schedules/schedule.component.ts index bd5b4942..edac2ab0 100644 --- a/client/src/app/schedules/schedule.component.ts +++ b/client/src/app/schedules/schedule.component.ts @@ -1,7 +1,7 @@ import {Component, Input, OnInit} from '@angular/core'; import {ScheduleService} from './schedule.service'; import {NgForm} from '@angular/forms'; -import {BaseComponent} from '../common/base-component'; +import {BaseComponent} from '@common/base-component'; import {takeUntil} from 'rxjs/operators'; @Component({ diff --git a/client/src/app/schedules/schedule.service.ts b/client/src/app/schedules/schedule.service.ts index 00c6a712..ed8e9046 100644 --- a/client/src/app/schedules/schedule.service.ts +++ b/client/src/app/schedules/schedule.service.ts @@ -1,6 +1,6 @@ import {throwError as observableThrowError, Observable} from 'rxjs'; import {Inject, Injectable} from '@angular/core'; -import {HttpService} from '../common/http.service'; +import {HttpService} from '@common/http.service'; import {environment} from '../../environments/environment'; import {catchError, map} from 'rxjs/operators'; import {SitesModule} from '../sites/sites.module'; @@ -40,7 +40,7 @@ export class ScheduleService { handleError(error) { console.error(error); - return observableThrowError(error || 'Server error'); + return observableThrowError(() => error ?? 'Server error'); } diff --git a/client/src/app/setup/setup.component.ts b/client/src/app/setup/setup.component.ts index 9a056c74..1b5b0502 100644 --- a/client/src/app/setup/setup.component.ts +++ b/client/src/app/setup/setup.component.ts @@ -3,7 +3,7 @@ import {Router} from '@angular/router'; import {SetupService} from './setup.service'; import {SetupConfig, SetupConfigForm} from './setup-config.model'; import {Subject} from 'rxjs'; -import {AuthService} from '../common/auth/auth.service'; +import {AuthService} from '@common/auth/auth.service'; import {FormControl, FormGroup} from "@angular/forms"; diff --git a/client/src/app/setup/setup.service.ts b/client/src/app/setup/setup.service.ts index f4ad239b..0a97cc31 100644 --- a/client/src/app/setup/setup.service.ts +++ b/client/src/app/setup/setup.service.ts @@ -1,12 +1,9 @@ import {Injectable, Inject} from '@angular/core'; import {environment} from '../../environments/environment'; - - - import {SetupConfig} from './setup-config.model'; -import {GenericService} from '../common/generic.service'; -import {HttpService} from '../common/http.service'; +import {GenericService} from '@common/generic.service'; +import {HttpService} from '@common/http.service'; import {catchError, map} from 'rxjs/operators'; @Injectable() diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts index f6e695a6..a28725c2 100644 --- a/client/src/app/shared/shared.module.ts +++ b/client/src/app/shared/shared.module.ts @@ -4,7 +4,7 @@ import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import {NgxDatatableModule} from '@swimlane/ngx-datatable'; import {FaIconLibrary, FontAwesomeModule} from '@fortawesome/angular-fontawesome'; import {fas} from '@fortawesome/free-solid-svg-icons'; -import {AsyncButtonDirective} from '../common/ui/async-button.directive'; +import {AsyncButtonDirective} from '@common/ui/async-button.directive'; import {BootstrapModule} from './bootstrap.module'; import {NgxBootstrapIconsModule} from "ngx-bootstrap-icons"; import {PageHeaderComponent} from "./components/page-header/page-header.component"; diff --git a/client/src/app/sites/site-edit.component.html b/client/src/app/sites/site-edit.component.html new file mode 100644 index 00000000..e0a35b1b --- /dev/null +++ b/client/src/app/sites/site-edit.component.html @@ -0,0 +1,13 @@ +

Site Form

+ + + + + + + + + + +
+ diff --git a/client/src/app/sites/site-edit.component.ts b/client/src/app/sites/site-edit.component.ts new file mode 100644 index 00000000..2605634e --- /dev/null +++ b/client/src/app/sites/site-edit.component.ts @@ -0,0 +1,77 @@ +import {ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; +import {FormBuilder, FormGroup, Validators} from '@angular/forms'; +import {Observable} from 'rxjs'; +import {Store} from '@ngrx/store'; +import {Update} from '@ngrx/entity'; +import {AppState} from '../store/app.state'; +import {ISite, Site} from './site.model'; +import {SiteActions} from './store'; + +@Component({ + selector: 'site-edit', + templateUrl: './site-edit.component.html', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class SiteEditComponent implements OnInit { + @Input() site: ISite; + @Input() mode: 'create' | 'update'; + @Output() formClosed = new EventEmitter(); + form: FormGroup; + dialogTitle: string; + loading$: Observable; + + constructor( + private fb: FormBuilder, + private store: Store + ) { + + } + + ngOnInit(): void { + const formControls = { + name: ['', Validators.required], + code: ['', Validators.required], + domain: [''], + }; + + if (this.mode === 'update') { + this.form = this.fb.group(formControls); + this.form.patchValue({...this.site}); + } + else if (this.mode === 'create') { + this.form = this.fb.group({ + ...formControls + }); + } + } + + onCancel() { + this.formClosed.emit(); + } + + onSave() { + const formValue = this.form.value; + const mergedSite: ISite = { + ...this.site, + ...formValue + }; + + const site = new Site(mergedSite); + // ngRx does not allow objects with their own constructors when strict serializability is turned on!!!!! + const serializableSite = {...site}; + + if (this.mode === 'create') { + this.store.dispatch(SiteActions.createSiteButtonClicked({site: serializableSite})); + } + else if (this.mode === 'update') { + this.store.dispatch(SiteActions.updateSiteButtonClicked({site: serializableSite})); + } + + this.formClosed.emit(); + } + + onDelete(site: ISite) { + this.store.dispatch(SiteActions.deleteSiteButtonClicked({site: {...site}})); + } + +} diff --git a/client/src/app/sites/site-list.component.html b/client/src/app/sites/site-list.component.html index 63beec08..5d98a534 100644 --- a/client/src/app/sites/site-list.component.html +++ b/client/src/app/sites/site-list.component.html @@ -1,13 +1,32 @@

Sites

-
- You haven't added any sites yet. Add a site! -
+ + +
+ +
+
- + + You haven't added any sites yet. Add a site! + +
- + +
+ loading... +
+
+ +
+ + + + + diff --git a/client/src/app/sites/site-list.component.ts b/client/src/app/sites/site-list.component.ts index 0602f8d3..fbc469ee 100644 --- a/client/src/app/sites/site-list.component.ts +++ b/client/src/app/sites/site-list.component.ts @@ -1,35 +1,46 @@ import {Component, OnInit} from '@angular/core'; -import {Site} from "./site.model"; -import {SiteService} from "./site.service"; -import {Router} from "@angular/router"; +import {ISite, Site} from './site.model'; +import {SiteActions, SiteSelectors} from './store'; +import {Store} from '@ngrx/store'; +import {AppState} from '../store/app.state'; +import {Observable} from 'rxjs'; @Component({ selector: 'kz-site-list', templateUrl: './site-list.component.html' }) export class SiteListComponent implements OnInit { + sites$: Observable = this.store.select(SiteSelectors.selectAllSites); + loading$: Observable = this.store.select(SiteSelectors.isLoading); + loaded$: Observable = this.store.select(SiteSelectors.isLoaded); + editing = false; + adding = false; + selectedSite: ISite; - sites: Site[] = []; - loading = true; - - constructor(private siteService: SiteService, private router: Router) { + constructor(private store: Store) { } ngOnInit() { - this.siteService.getAll() - .subscribe( - (sites) => { - this.sites = sites; - this.loading = false; - }, - (error) => { - this.loading = false; - } - ); + this.store.dispatch(SiteActions.siteListComponentOpened()); + } + + onEdit(site: ISite) { + this.selectedSite = {...site}; + this.editing = true; + } + + onAdd() { + this.adding = true; + } + + onDelete(site: ISite) { + this.store.dispatch(SiteActions.deleteSiteButtonClicked({ site })); } - create() { - this.router.navigateByUrl('sites/create'); + onFormClosed() { + this.selectedSite = null; + this.editing = false; + this.adding = false; } } diff --git a/client/src/app/sites/site.component.html b/client/src/app/sites/site.component.html index 8bba95e9..2033cc76 100644 --- a/client/src/app/sites/site.component.html +++ b/client/src/app/sites/site.component.html @@ -1,38 +1,13 @@ -
-
-
- - -
+
+

This detail page requires no further styling - you cannot improve upon perfection.

+

Name

+ {{site.name}} +

Code

+ {{site.code}} +

Domain

+ {{site.domain}}
+
+ +
-
- - -
-
- - -
- -
- - -
- -
-
- - diff --git a/client/src/app/sites/site.component.ts b/client/src/app/sites/site.component.ts index b8149c72..5d01c3f3 100644 --- a/client/src/app/sites/site.component.ts +++ b/client/src/app/sites/site.component.ts @@ -1,13 +1,13 @@ import {Component, OnInit} from '@angular/core'; -import {ActivatedRoute, Params, Router} from '@angular/router'; -import {NgForm} from '@angular/forms'; -import {Site} from './site.model'; -import {SiteService} from './site.service'; -import {BaseComponent} from '../common/base-component'; -import {Observable, of} from 'rxjs'; +import {ActivatedRoute, Params} from '@angular/router'; +import {ISite, Site} from './site.model'; +import {BaseComponent} from '@common/base-component'; +import {Observable} from 'rxjs'; -import * as _ from 'lodash-es'; -import {flatMap, takeUntil} from 'rxjs/operators'; +import {tap} from 'rxjs/operators'; +import {Store} from '@ngrx/store'; +import {AppState} from '../store/app.state'; +import {SiteSelectors, SiteState} from './store'; @Component({ selector: 'kz-site', @@ -15,91 +15,14 @@ import {flatMap, takeUntil} from 'rxjs/operators'; }) export class SiteComponent extends BaseComponent implements OnInit { - site: Site = new Site(); - saving = false; - original = {}; - siteId: string; - isCreate = false; - - constructor(private route: ActivatedRoute, private siteService: SiteService, private router: Router) { + site$: Observable; + constructor(private route: ActivatedRoute, private store: Store) { super(); } ngOnInit() { - this.route.params - .pipe( - flatMap((params: Params) => { - const id = params['id'] || ''; - if (id) { - this.siteId = id; - return this.siteService.getById(this.siteId); - } - else { - return of(null); - } - }) - ) - .subscribe((site) => { - if (site) { - this.site = site; - this.original = Object.assign({}, this.site); - } - else { - this.isCreate = true; - this.site = new Site(); - } - }); - + const id = this.route.snapshot.paramMap.get('id'); + this.site$ = this.store.select(SiteSelectors.selectSiteById(id)); } - save(form: NgForm) { - // validate form - if (!form.valid) { - return; - } - - this.saving = true; - - if (this.isCreate) { - this.siteService.create(form.value) - .pipe( - takeUntil(this.ngUnsubscribe) - ) - .subscribe( - (result) => { - this.saving = false; - this.router.navigateByUrl('sites'); - }, - (error) => { - this.saving = false; - } - ); - } - else { - this.siteService.update(this.siteId, form.value) - .pipe( - takeUntil(this.ngUnsubscribe) - ) - .subscribe((result) => { - this.saving = false; - this.original = Object.assign({}, this.site); - form.form.markAsPristine(); - }); - } - } - - cancel(form: NgForm) { - if (this.isCreate) { - this.router.navigateByUrl('sites'); - } - else { - this.site = Object.assign({}, new Site(this.original)); - form.form.markAsPristine(); - } - } - - onNameChange(newName: string) { - const snakeCasedName = _.snakeCase(newName); - this.site.code = snakeCasedName; - } } diff --git a/client/src/app/sites/site.model.ts b/client/src/app/sites/site.model.ts index 131fb486..dd20fc79 100644 --- a/client/src/app/sites/site.model.ts +++ b/client/src/app/sites/site.model.ts @@ -1,23 +1,36 @@ +export interface ISite { + id: string; + orgId: string; + code: string; + name: string; + domain: string; +} + export class Site { id: string; orgId: string; code: string; name: string; - domainName: string; + domain: string; constructor(options: { id?: string, orgId?: string, code?: string, name?: string, - domainName?: string, + domain?: string, } = {}) { this.id = options.id; this.orgId = options.orgId; - this.code = options.code || ''; - this.name = options.name || ''; - this.domainName = options.domainName || ''; + this.code = options.code ?? ''; + this.name = options.name ?? ''; + this.domain = options.domain ?? ''; } } +export function compareSites(a: ISite, b: ISite) { + // a decent way to sort strings (it might work for numbers too) + return +(a.code > b.code) || -(a.code < b.code) +} + diff --git a/client/src/app/sites/site.service.ts b/client/src/app/sites/site.service.ts index 172c5a53..0cc1fb36 100644 --- a/client/src/app/sites/site.service.ts +++ b/client/src/app/sites/site.service.ts @@ -5,8 +5,8 @@ import {environment} from '../../environments/environment'; import {Site} from './site.model'; -import {GenericService} from '../common/generic.service'; -import {HttpService} from '../common/http.service'; +import {GenericService} from '@common/generic.service'; +import {HttpService} from '@common/http.service'; import {catchError, map} from 'rxjs/operators'; @Injectable() diff --git a/client/src/app/sites/sites-routing.module.ts b/client/src/app/sites/sites-routing.module.ts index b83cbfd1..201e3339 100644 --- a/client/src/app/sites/sites-routing.module.ts +++ b/client/src/app/sites/sites-routing.module.ts @@ -1,13 +1,18 @@ import {NgModule} from '@angular/core'; import {Routes, RouterModule} from '@angular/router'; import {SiteListComponent} from './site-list.component'; +import {SiteComponent} from './site.component'; const routes: Routes = [ { path: '', component: SiteListComponent - } + }, + { + path: ':id', + component: SiteComponent + }, ]; @NgModule({ diff --git a/client/src/app/sites/sites.module.ts b/client/src/app/sites/sites.module.ts index 4cb2bbec..ef696b2e 100644 --- a/client/src/app/sites/sites.module.ts +++ b/client/src/app/sites/sites.module.ts @@ -1,23 +1,29 @@ import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; +import {StoreModule} from '@ngrx/store'; +import {EffectsModule} from '@ngrx/effects'; import {SitesRoutingModule} from './sites-routing.module'; import {SiteListComponent} from './site-list.component'; import {SiteComponent} from './site.component'; import {SharedModule} from '../shared/shared.module'; import {SchedulesComponent} from '../schedules/schedule.component'; - +import {SiteEditComponent} from './site-edit.component'; +import {SiteEffects, siteReducer, sitesFeatureKey} from './store'; @NgModule({ declarations: [ SiteListComponent, SiteComponent, + SiteEditComponent, SchedulesComponent ], imports: [ CommonModule, SharedModule, - SitesRoutingModule + SitesRoutingModule, + StoreModule.forFeature(sitesFeatureKey, siteReducer, { }), + EffectsModule.forFeature([SiteEffects]), ] }) export class SitesModule { diff --git a/client/src/app/sites/store/actions/index.ts b/client/src/app/sites/store/actions/index.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/client/src/app/sites/store/effects/index.ts b/client/src/app/sites/store/effects/index.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/client/src/app/sites/store/index.ts b/client/src/app/sites/store/index.ts index e69de29b..86902b18 100644 --- a/client/src/app/sites/store/index.ts +++ b/client/src/app/sites/store/index.ts @@ -0,0 +1,6 @@ +import { siteReducer, sitesFeatureKey, SiteState } from './site.reducer'; +import { SiteEffects } from './site.effects'; +import * as SiteSelectors from './site.selectors'; +import * as SiteActions from './site.actions'; + +export { siteReducer, sitesFeatureKey, SiteState, SiteEffects, SiteSelectors, SiteActions }; diff --git a/client/src/app/sites/store/reducers/index.ts b/client/src/app/sites/store/reducers/index.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/client/src/app/sites/store/selectors/index.ts b/client/src/app/sites/store/selectors/index.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/client/src/app/sites/store/site.actions.ts b/client/src/app/sites/store/site.actions.ts new file mode 100644 index 00000000..b1409020 --- /dev/null +++ b/client/src/app/sites/store/site.actions.ts @@ -0,0 +1,19 @@ +import {createAction, props} from '@ngrx/store'; +import {ISite} from '../site.model'; + +export const siteListComponentOpened = createAction('[SiteListComponent] Opened'); +export const sitesLoaded = createAction('[SiteEffects] Sites Loaded', props<{sites: ISite[]}>()); +export const getAllSitesFailed = createAction('[SiteEffects] GetAllSites Failed', props<{ error: Error }>()); + +export const createSiteButtonClicked = createAction('[SiteListComponent] CreateSiteButton Clicked', props<{site: ISite}>()); +export const siteCreated = createAction('[SiteEffects] Site Created', props<{site: ISite}>()); +export const createSiteFailed = createAction('[SiteEffects] Create Site Failed', props<{ error: Error }>()); + +export const updateSiteButtonClicked = createAction('[SiteEditComponent] UpdateSiteButton Clicked', props<{site: ISite}>()); +export const siteUpdated = createAction('[SiteEffects] Site Updated', props<{site: ISite}>()); +export const updateSiteFailed = createAction('[SiteEffects] Update Site Failed', props<{ error: Error }>()); + +export const deleteSiteButtonClicked = createAction('[SiteEditComponent] DeleteSiteButton Clicked', props<{site: ISite}>()); +export const siteDeleted = createAction('[SiteEffects] Site Deleted', props<{site: ISite}>()); +export const deleteSiteFailed = createAction('[SiteEffects] Delete Site Failed', props<{ error: Error }>()); + diff --git a/client/src/app/sites/store/site.effects.ts b/client/src/app/sites/store/site.effects.ts new file mode 100644 index 00000000..71f4d8d3 --- /dev/null +++ b/client/src/app/sites/store/site.effects.ts @@ -0,0 +1,79 @@ +import {Injectable} from '@angular/core'; +import {Actions, createEffect, ofType} from '@ngrx/effects'; +import {Store} from '@ngrx/store'; +import {catchError, combineLatestWith, delay, map, switchMap} from 'rxjs/operators'; +import {of as observableOf} from 'rxjs'; +import {SiteService} from '../site.service'; +import {SiteActions} from './index'; + +@Injectable() +export class SiteEffects { + constructor(private actions$: Actions, + private siteService: SiteService, + private store: Store) {} + + getAllSites$ = createEffect(() => + this.actions$.pipe( + ofType(SiteActions.siteListComponentOpened), + switchMap((action) => { + return this.siteService.getAll() + .pipe( + delay(3000), + map((sites) => SiteActions.sitesLoaded({ sites })), + catchError((error) => { + return observableOf(SiteActions.getAllSitesFailed({ error })); + }) + ); + }), + ) + ); + + createSite$ = createEffect(() => + this.actions$.pipe( + ofType(SiteActions.createSiteButtonClicked), + switchMap((action) => { + return this.siteService.create(action.site) + .pipe( + map((site) => SiteActions.siteCreated({ site })), + catchError((error) => { + return observableOf(SiteActions.createSiteFailed({ error })); + }) + ); + }), + ) + ); + + updateSite$ = createEffect(() => + this.actions$.pipe( + ofType(SiteActions.updateSiteButtonClicked), + switchMap((action) => { + return this.siteService.update(action.site.id, action.site) + .pipe( + map((site) => { + return SiteActions.siteUpdated({ site }) + }), + catchError((error) => { + return observableOf(SiteActions.updateSiteFailed({ error })); + }) + ); + }), + ) + ); + + deleteSite$ = createEffect(() => + this.actions$.pipe( + ofType(SiteActions.deleteSiteButtonClicked), + switchMap((action) => { + return this.siteService.delete(action.site.id) + .pipe( + combineLatestWith(observableOf(action)), + // we return the site from the action because the api doesn't return anything on success + map(([deleteResult, action]) => SiteActions.siteDeleted({ site: action.site })), + catchError((error) => { + return observableOf(SiteActions.deleteSiteFailed({ error })); + }) + ); + }), + ) + ); +} diff --git a/client/src/app/sites/store/site.reducer.ts b/client/src/app/sites/store/site.reducer.ts new file mode 100644 index 00000000..27a69fdf --- /dev/null +++ b/client/src/app/sites/store/site.reducer.ts @@ -0,0 +1,59 @@ +import {createReducer, on} from '@ngrx/store'; +import {createEntityAdapter, EntityState} from '@ngrx/entity'; +import {compareSites, ISite} from '../site.model'; +import * as SiteActions from './site.actions'; +import NgrxUtils from '@common/utils/ngrx.utils'; + +export const sitesFeatureKey = 'sites'; + +export interface SiteState extends EntityState { + loading: boolean, + loaded: boolean +} + +// since we are using NgRx's Entity module (great for collections of things), we create an adapter - a helper that makes it easier +// to add, update, and remove items from the collection. +// https://blog.angular-university.io/ngrx-entity/ tl;dr Entity provides an efficient means of storing a collection of items, allowing +// fast lookups by key and efficient sorting via an array. +export const adapter = createEntityAdapter({ + sortComparer: compareSites, + //selectId: site => site.name // this is where you would override your distinct id if it's not 'id'. If you have an id though, use it. +}); + +const initialSiteState = adapter.getInitialState({ + loading: false, + loaded: false +}); + +export const siteReducer = createReducer( + { ...initialSiteState }, + + // this assumes we always call the api to load sites when the list component is opened. If we change to conditionally + // load, we can put the condition in a utility or service and use that condition here and in the effect. + on(SiteActions.siteListComponentOpened, (state, action) => { + return { ...state, loading: true } + }), + + on(SiteActions.sitesLoaded, (state, action) => { + return adapter.setAll(action.sites, + {...state, loading: false, loaded: true}); + }), + + on(SiteActions.siteCreated, (state, { site }) => { + return adapter.addOne(site, state); + }), + + on(SiteActions.siteUpdated, (state, { site }) => { + const update = NgrxUtils.getUpdateObjectFor(site); + return adapter.updateOne(update, state); + }), + + on(SiteActions.siteDeleted, (state, { site }) => { + return adapter.removeOne(site.id, state); + }), + +); + +export const { + selectAll, selectEntities, selectIds, selectTotal +} = adapter.getSelectors(); diff --git a/client/src/app/sites/store/site.selectors.ts b/client/src/app/sites/store/site.selectors.ts new file mode 100644 index 00000000..19feedc2 --- /dev/null +++ b/client/src/app/sites/store/site.selectors.ts @@ -0,0 +1,26 @@ +import {createFeatureSelector, createSelector} from '@ngrx/store'; +import {sitesFeatureKey, SiteState, selectAll, selectEntities, selectIds, selectTotal} from './site.reducer'; + +export const selectSiteState = createFeatureSelector(sitesFeatureKey); + +export const selectAllSites = createSelector( + selectSiteState, + selectAll +); + +export const selectSiteById = (id: string) => createSelector( + selectSiteState, + (sites) => { + return sites.entities[id]; + } +); + +export const isLoading = createSelector( + selectSiteState, + state => state.loading +); + +export const isLoaded = createSelector( + selectSiteState, + state => state.loaded +); diff --git a/client/src/app/store/app.reducer.ts b/client/src/app/store/app.reducer.ts index ab19a669..e4f3cbf9 100644 --- a/client/src/app/store/app.reducer.ts +++ b/client/src/app/store/app.reducer.ts @@ -1,7 +1,7 @@ // These property names have to match AppState property names import {ActionReducerMap, MetaReducer} from '@ngrx/store'; import {AppState} from './app.state'; -import {authReducer} from '../common/auth/store'; +import {authReducer} from '@common/auth/store'; export const appReducer: ActionReducerMap = { auth: authReducer, diff --git a/client/src/app/store/app.state.ts b/client/src/app/store/app.state.ts index ac612577..312d6fc2 100644 --- a/client/src/app/store/app.state.ts +++ b/client/src/app/store/app.state.ts @@ -1,4 +1,4 @@ -import {IUserContext} from '../common/auth/user-context.model'; +import {IUserContext} from '@common/auth/user-context.model'; export class AppState { auth: IUserContext diff --git a/client/src/app/templates/template-details/template.component.ts b/client/src/app/templates/template-details/template.component.ts index 89ac0485..c352425e 100644 --- a/client/src/app/templates/template-details/template.component.ts +++ b/client/src/app/templates/template-details/template.component.ts @@ -4,7 +4,7 @@ import {FormArray, FormControl, FormGroup, Validators} from '@angular/forms'; import * as _ from 'lodash-es'; import {takeUntil} from 'rxjs/operators'; import {Site} from "../../sites/site.model"; -import {BaseComponent} from "../../common/base-component"; +import {BaseComponent} from "@common/base-component"; import {SiteService} from "../../sites/site.service"; import {TemplateService} from "../template.service"; import {Template} from "../template.model"; diff --git a/client/src/app/templates/template.service.ts b/client/src/app/templates/template.service.ts index 02ee7e7a..fd908abe 100644 --- a/client/src/app/templates/template.service.ts +++ b/client/src/app/templates/template.service.ts @@ -3,8 +3,8 @@ import {environment} from '../../environments/environment'; import {Template} from './template.model'; -import {GenericService} from '../common/generic.service'; -import {HttpService} from '../common/http.service'; +import {GenericService} from '@common/generic.service'; +import {HttpService} from '@common/http.service'; import {catchError, map} from 'rxjs/operators'; @Injectable({ diff --git a/client/src/app/templates/templates-routing.module.ts b/client/src/app/templates/templates-routing.module.ts index f0bd1de9..3a5bfd18 100644 --- a/client/src/app/templates/templates-routing.module.ts +++ b/client/src/app/templates/templates-routing.module.ts @@ -1,7 +1,7 @@ import {NgModule} from '@angular/core'; import {RouterModule, Routes} from '@angular/router'; import {TemplatesComponent} from './templates.component'; -import {KazukuAuthGuardService} from "../common/auth/kazuku-auth-guard.service"; +import {KazukuAuthGuardService} from "@common/auth/kazuku-auth-guard.service"; import {TemplateComponent} from "./template-details/template.component"; const routes: Routes = [ diff --git a/client/src/app/users/user-list.component.ts b/client/src/app/users/user-list.component.ts index 20c32402..02a6f1ad 100644 --- a/client/src/app/users/user-list.component.ts +++ b/client/src/app/users/user-list.component.ts @@ -1,5 +1,5 @@ import {Component, OnInit} from '@angular/core'; -import {User} from '../common/auth/user.model'; +import {User} from '@common/auth/user.model'; import {Router} from '@angular/router'; import {UserService} from './user.service'; diff --git a/client/src/app/users/user.service.ts b/client/src/app/users/user.service.ts index 48180a69..6a2bc47f 100644 --- a/client/src/app/users/user.service.ts +++ b/client/src/app/users/user.service.ts @@ -1,10 +1,10 @@ import {Injectable, Inject} from '@angular/core'; import {environment} from '../../environments/environment'; -import {GenericService} from '../common/generic.service'; -import {HttpService} from '../common/http.service'; +import {GenericService} from '@common/generic.service'; +import {HttpService} from '@common/http.service'; import {catchError, map} from 'rxjs/operators'; -import {User} from '../common/auth/user.model'; +import {User} from '@common/auth/user.model'; @Injectable({ providedIn: 'root' diff --git a/client/tsconfig.json b/client/tsconfig.json index 2d7e3fbb..3c41a768 100644 --- a/client/tsconfig.json +++ b/client/tsconfig.json @@ -23,7 +23,10 @@ "lib": [ "es2020", "dom" - ] + ], + "paths": { + "@common/*": ["src/app/common/*"] + } }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/server/src/common/crudController.js b/server/src/common/crudController.js index 4dfba290..28a97c8c 100644 --- a/server/src/common/crudController.js +++ b/server/src/common/crudController.js @@ -83,10 +83,8 @@ class CrudController { let body = req.body; this.service.updateById(current.context.orgId, id, body) - .then((result) => { - if (result.nModified <= 0) return next(); - - return res.status(200).json({}); + .then((doc) => { + return res.status(200).json(doc); }) .catch(err => { if (err.constructor == TypeError) { diff --git a/server/src/common/genericService.js b/server/src/common/genericService.js index 6afbe88f..89bbcac3 100644 --- a/server/src/common/genericService.js +++ b/server/src/common/genericService.js @@ -104,15 +104,19 @@ class GenericService { } conversionService.convertISOStringDateTimesToMongoDates(doc); + let insertedDoc; return this.onBeforeCreate(orgId, doc) .then((result) => { return this.collection.insert(doc) }) - .then((doc) => { - this.useFriendlyId(doc); - return this.onAfterCreate(orgId, doc) - .then(() => { return doc }); // ignore the result of onAfter and return what the original call returned + .then((insertResult) => { + insertedDoc = insertResult; + this.useFriendlyId(insertedDoc); + return this.onAfterCreate(orgId, insertedDoc); + }) + .then((afterCreateResult) => { + return insertedDoc; // ignore the result of onAfter and return the insertedDoc }); } @@ -133,9 +137,12 @@ class GenericService { .then((result) => { return this.collection.update(queryObject, {$set: clone}) }) - .then((result) => { - return this.onAfterUpdate(orgId, clone) - .then(() => { return result }); // ignore the result of onAfter and return what the original call returned + .then((mongoUpdateResult) => { + return this.onAfterUpdate(orgId, clone); + }) + .then((afterUpdateResult) => { + clone.id = id; // add the friendly string id back to be returned + return clone; // ignore the result of onAfter and return what the original call returned }); } @@ -172,8 +179,11 @@ class GenericService { return this.collection.update(mongoQueryObject, {$set: clone}); }) .then((result) => { - return this.onAfterUpdate(orgId, clone) - .then(() => { return result }); // ignore the result of onAfter and return what the original call returned + return this.onAfterUpdate(orgId, clone); + }) + .then((afterUpdateResult) => { + clone.id = id; // add the friendly string id back to be returned + return clone; // ignore the result of onAfter and return what the original call returned }); } @@ -200,13 +210,18 @@ class GenericService { } let queryObject = { _id: new ObjectId(id), orgId: orgId }; + let removeResult; + return this.onBeforeDelete(orgId, queryObject) .then((result) => { return this.collection.remove(queryObject) }) - .then((result) => { - return this.onAfterDelete(orgId, queryObject) - .then(() => { return result }); // ignore the result of onAfter and return what the original call returned + .then((mongoRemoveResult) => { + removeResult = mongoRemoveResult; + return this.onAfterDelete(orgId, queryObject); + }) + .then((afterDeleteResult) => { + return removeResult; // ignore the result of onAfter and return what the remove call returned }); } @@ -236,7 +251,8 @@ class GenericService { auditForCreate(doc) { const now = moment().utc().toDate(); - const userId = current.context && current.context.current && current.context.current.user ? current.context.user.email : 'system'; + // const userId = current.context && current.context.current && current.context.current.user ? current.context.user.email : 'system'; + const userId = current.context.user.email; doc.created = now; doc.createdBy = userId; doc.updated = now;