From 6f363c0df738dec1cfb1940e662e17445c935082 Mon Sep 17 00:00:00 2001 From: GCham5 Date: Wed, 16 Nov 2022 18:12:58 -0500 Subject: [PATCH 1/7] feat(frontend): refactor actions into sharedStore --- .../resource-profile.component.ts | 60 +++++--- .../lib/+state/resource/resource.reducers.ts | 2 + .../src/lib/+state/auth/auth.actions.ts | 8 +- .../src/lib/+state/hydration.reducer.ts | 10 +- .../data-access/src/lib/+state/index.ts | 1 + .../src/lib/+state/onboardingClient.state.ts | 3 + .../src/lib/+state/resource/index.ts | 4 + .../lib/+state/resource/resource.actions.ts | 98 +++++++++++++ .../lib/+state/resource/resource.effects.ts | 126 ++++++++++++++++ .../lib/+state/resource/resource.reducers.ts | 135 ++++++++++++++++++ .../lib/+state/resource/resource.selectors.ts | 37 +++++ ...arding-client-shared-data-access.module.ts | 4 +- 12 files changed, 465 insertions(+), 23 deletions(-) create mode 100644 libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/index.ts create mode 100644 libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.actions.ts create mode 100644 libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.effects.ts create mode 100644 libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.reducers.ts create mode 100644 libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.selectors.ts diff --git a/libs/client/onboarding-client/business-owner/features/feature-view-resource-profile/src/lib/resource-profile/resource-profile.component.ts b/libs/client/onboarding-client/business-owner/features/feature-view-resource-profile/src/lib/resource-profile/resource-profile.component.ts index be4205c6..6da6b6d4 100644 --- a/libs/client/onboarding-client/business-owner/features/feature-view-resource-profile/src/lib/resource-profile/resource-profile.component.ts +++ b/libs/client/onboarding-client/business-owner/features/feature-view-resource-profile/src/lib/resource-profile/resource-profile.component.ts @@ -1,12 +1,14 @@ -import { ChangeDetectorRef, Component, Input, OnInit, ViewChild } from '@angular/core'; +import { ChangeDetectorRef, Component, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { Store } from '@ngrx/store'; import { OnboardingClientResourceService, OnboardingClientState, + getResourceInformationById, + selectResourceDetails, } from '@tempus/client/onboarding-client/shared/data-access'; import { LoadView, ProjectResource } from '@tempus/shared-domain'; -import { skip, take } from 'rxjs'; +import { skip, Subject, take, takeUntil } from 'rxjs'; import { BusinessOwnerState, getOriginalResume, @@ -19,7 +21,7 @@ import { EditViewFormComponent } from '@tempus/onboarding-client/shared/feature- templateUrl: './resource-profile.component.html', styleUrls: ['./resource-profile.component.scss'], }) -export class ResourceProfileComponent implements OnInit { +export class ResourceProfileComponent implements OnInit, OnDestroy { constructor( private route: ActivatedRoute, private router: Router, @@ -33,6 +35,8 @@ export class ResourceProfileComponent implements OnInit { @Input() editViewEnabled = false; + $destroyed = new Subject(); + resourceId = 0; resourceFirstName = ''; @@ -110,19 +114,38 @@ export class ResourceProfileComponent implements OnInit { ngOnInit(): void { this.resourceId = parseInt(this.route.snapshot.paramMap.get('id') || '0', 10); - this.resourceService.getResourceInformationById(this.resourceId).subscribe(resourceInfo => { - this.resourceFirstName = resourceInfo.firstName; - this.resourceLastName = resourceInfo.lastName; - this.city = resourceInfo.location.city; - this.state = resourceInfo.location.province; - this.country = resourceInfo.location.country; - this.phoneNumber = resourceInfo.phoneNumber; - this.resourceEmail = resourceInfo.email; - this.linkedInLink = resourceInfo.linkedInLink; - this.githubLink = resourceInfo.githubLink; - this.otherLink = resourceInfo.otherLink; - this.projectResources = resourceInfo.projectResources; - }); + + this.sharedStore.dispatch(getResourceInformationById({ resourceId: this.resourceId })); + this.sharedStore + .select(selectResourceDetails) + .pipe(skip(1), takeUntil(this.$destroyed)) + .subscribe(data => { + this.resourceFirstName = data.firstName || ''; + this.resourceLastName = data.lastName || ''; + this.city = data.city || ''; + this.state = data.province || ''; + this.country = data.country || ''; + this.phoneNumber = data.phoneNumber || ''; + this.resourceEmail = data.email || ''; + this.linkedInLink = data.linkedInLink || ''; + this.githubLink = data.githubLink || ''; + this.otherLink = data.otherLink || ''; + this.projectResources = data.projectResources || []; + }); + + // this.resourceService.getResourceInformationById(this.resourceId).subscribe(resourceInfo => { + // this.resourceFirstName = resourceInfo.firstName; + // this.resourceLastName = resourceInfo.lastName; + // this.city = resourceInfo.location.city; + // this.state = resourceInfo.location.province; + // this.country = resourceInfo.location.country; + // this.phoneNumber = resourceInfo.phoneNumber; + // this.resourceEmail = resourceInfo.email; + // this.linkedInLink = resourceInfo.linkedInLink; + // this.githubLink = resourceInfo.githubLink; + // this.otherLink = resourceInfo.otherLink; + // this.projectResources = resourceInfo.projectResources; + // }); this.businessOwnerStore.dispatch(getOriginalResume({ resourceId: this.resourceId })); this.businessOwnerStore @@ -134,4 +157,9 @@ export class ResourceProfileComponent implements OnInit { } }); } + + ngOnDestroy(): void { + this.$destroyed.next(); + this.$destroyed.complete(); + } } diff --git a/libs/client/onboarding-client/resource/data-access/src/lib/+state/resource/resource.reducers.ts b/libs/client/onboarding-client/resource/data-access/src/lib/+state/resource/resource.reducers.ts index b309cf08..650da683 100644 --- a/libs/client/onboarding-client/resource/data-access/src/lib/+state/resource/resource.reducers.ts +++ b/libs/client/onboarding-client/resource/data-access/src/lib/+state/resource/resource.reducers.ts @@ -12,6 +12,7 @@ export interface ResourceState { views: View[] | null; resume: Blob | null; totalViewsData: number; + error: Error | null; } export const initialState: ResourceState = { @@ -21,6 +22,7 @@ export const initialState: ResourceState = { views: null, resume: null, totalViewsData: 0, + error: null, }; export const resourceReducer = createReducer( diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/+state/auth/auth.actions.ts b/libs/client/onboarding-client/shared/data-access/src/lib/+state/auth/auth.actions.ts index c7004069..fa6e802a 100644 --- a/libs/client/onboarding-client/shared/data-access/src/lib/+state/auth/auth.actions.ts +++ b/libs/client/onboarding-client/shared/data-access/src/lib/+state/auth/auth.actions.ts @@ -16,10 +16,10 @@ export const loginSuccess = createAction( }>(), ); export const loginFailure = createAction('[Onboarding Client Auth Api] Login Failure', props<{ error: Error }>()); -export const updateInfoFailure = createAction( - '[Onboarding Client Auth Api] Update Info Failure', - props<{ error: Error }>(), -); +// export const updateInfoFailure = createAction( +// '[Onboarding Client Auth Api] Update Info Failure', +// props<{ error: Error }>(), +// ); export const logoutSuccess = createAction('[Onboarding Client Auth Api] Logout Success'); export const logoutFailure = createAction('[Onboarding Client Auth Api] Logout Failure', props<{ error: Error }>()); export const logout = createAction('[Onboarding Client Any Page] Logout', props<{ redirect: boolean }>()); diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/+state/hydration.reducer.ts b/libs/client/onboarding-client/shared/data-access/src/lib/+state/hydration.reducer.ts index e33fab52..15cceeb0 100644 --- a/libs/client/onboarding-client/shared/data-access/src/lib/+state/hydration.reducer.ts +++ b/libs/client/onboarding-client/shared/data-access/src/lib/+state/hydration.reducer.ts @@ -19,7 +19,15 @@ export const hydrationMetaReducer = ( if (newState.auth) { newState = { ...newState, - auth: { ...newState.auth, accessToken, refreshToken, loggedInUserId: userId, firstName, lastName, email }, + auth: { + ...newState.auth, + accessToken, + refreshToken, + loggedInUserId: userId, + firstName, + lastName, + email, + }, }; } return reducer(newState as OnboardingClientState, action); diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/+state/index.ts b/libs/client/onboarding-client/shared/data-access/src/lib/+state/index.ts index 0deabf84..38c1fe95 100644 --- a/libs/client/onboarding-client/shared/data-access/src/lib/+state/index.ts +++ b/libs/client/onboarding-client/shared/data-access/src/lib/+state/index.ts @@ -1,2 +1,3 @@ export * from './auth'; export * from './onboardingClient.state'; +export * from './resource'; diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/+state/onboardingClient.state.ts b/libs/client/onboarding-client/shared/data-access/src/lib/+state/onboardingClient.state.ts index 2884ca55..107bbfb2 100644 --- a/libs/client/onboarding-client/shared/data-access/src/lib/+state/onboardingClient.state.ts +++ b/libs/client/onboarding-client/shared/data-access/src/lib/+state/onboardingClient.state.ts @@ -1,14 +1,17 @@ import { ActionReducerMap, createFeatureSelector } from '@ngrx/store'; import { authReducer, AuthState, AUTH_FEATURE_KEY } from './auth/auth.reducers'; +import { resourceReducer, ResourceState, RESOURCE_INFO_FEATURE_KEY } from './resource/resource.reducers'; export const ONBOARDING_CLIENT_FEATURE_KEY = 'onboardingClient'; export interface OnboardingClientState { [AUTH_FEATURE_KEY]: AuthState; + [RESOURCE_INFO_FEATURE_KEY]: ResourceState; } export const reducers: ActionReducerMap = { [AUTH_FEATURE_KEY]: authReducer, + [RESOURCE_INFO_FEATURE_KEY]: resourceReducer, }; export const selectOnboardingClientState = createFeatureSelector(ONBOARDING_CLIENT_FEATURE_KEY); diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/index.ts b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/index.ts new file mode 100644 index 00000000..9bcb86f6 --- /dev/null +++ b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/index.ts @@ -0,0 +1,4 @@ +export * from './resource.actions'; +export * from './resource.selectors'; +export * from './resource.effects'; +export * from './resource.reducers'; diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.actions.ts b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.actions.ts new file mode 100644 index 00000000..d816a62a --- /dev/null +++ b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.actions.ts @@ -0,0 +1,98 @@ +import { createAction, props } from '@ngrx/store'; +import { IUpdateResourceDto, ProjectResource, View } from '@tempus/shared-domain'; + +export const getResourceInformationById = createAction( + '[Onboarding Client User Api] Get Resource Information By Id', + props<{ resourceId: number }>(), +); + +export const getResourceInformationByIdSuccess = createAction( + '[Onboarding Client User Api] Get Resource Information By Id Success', + props<{ + firstName: string; + lastName: string; + email: string; + phoneNumber: string; + city: string; + province: string; + country: string; + linkedInLink: string; + githubLink: string; + otherLink: string; + projectResources: ProjectResource[]; + }>(), +); + +export const getResourceInformationByIdFailure = createAction( + '[Onboarding Client User Api] Get Resource Information By Id Failure', + props<{ error: Error }>(), +); + +export const updateUserInfo = createAction( + '[Onboarding Client Api] Update Info', + props<{ + updatedPersonalInformation: IUpdateResourceDto; + }>(), +); + +export const updateUserInfoSuccess = createAction( + '[Onboarding Client Api] Update Info Success', + props<{ + firstName: string; + lastName: string; + email: string; + }>(), +); + +export const updateInfoFailure = createAction( + '[Onboarding Client Auth Api] Update Info Failure', + props<{ error: Error }>(), +); + +// get all views by resource id +export const getAllViewsByResourceId = createAction( + '[Onboarding Client Profile Views API] Get All Views By Resource Id', + props<{ resourceId: number; pageNum: number; pageSize: number }>(), +); + +export const getAllViewsByResourceIdSuccess = createAction( + '[Onboarding Client Profile Views API] Get All Views By Resource Id Success', + props<{ views: View[]; totalViews: number }>(), +); + +export const getAllViewsByResourceIdFailure = createAction( + '[Onboarding Client Profile Views API] Get All Views By Resource Id Failure', + props<{ error: Error }>(), +); + +// get original resume by resource id +export const getResourceOriginalResumeById = createAction( + '[Onboarding Client User API] Get Original Resume By Resource Id', + props<{ resourceId: number }>(), +); + +export const getResourceOriginalResumeByIdSuccess = createAction( + '[Onboarding Client User API] Get Original Resume By Resource Id Success', + props<{ resume: Blob }>(), +); + +export const getResourceOriginalResumeByIdFailure = createAction( + '[Onboarding Client User API] Get Original Resume By Resource Id Failure', + props<{ error: Error }>(), +); + +// download profile/resume +export const downloadProfileByViewId = createAction( + '[Onboarding Client Profile Views API] Download Profile By View Id', + props<{ viewId: number }>(), +); + +export const downloadProfileByViewIdSuccess = createAction( + '[Onboarding Client Profile Views API] Download Profile By View Id Success', + props<{ resume: Blob }>(), +); + +export const downloadProfileByViewIdFailure = createAction( + '[Onboarding Client Profile Views API] Download Profile By View Id Failure', + props<{ error: Error }>(), +); diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.effects.ts b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.effects.ts new file mode 100644 index 00000000..a5a8c0dc --- /dev/null +++ b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.effects.ts @@ -0,0 +1,126 @@ +import { Actions, createEffect, ofType } from '@ngrx/effects'; +import { of, switchMap } from 'rxjs'; +import { map, catchError } from 'rxjs/operators'; +import { Store } from '@ngrx/store'; +import { Injectable } from '@angular/core'; +import { + OnboardingClientAuthService, + OnboardingClientResourceService, + OnboardingClientViewsService, +} from '../../services'; +import { + getAllViewsByResourceId, + getResourceInformationById, + getResourceInformationByIdSuccess, + getResourceOriginalResumeById, + updateInfoFailure, + updateUserInfo, + updateUserInfoSuccess, +} from './resource.actions'; +import { + downloadProfileByViewId, + downloadProfileByViewIdFailure, + downloadProfileByViewIdSuccess, + getAllViewsByResourceIdFailure, + getAllViewsByResourceIdSuccess, + getResourceInformationByIdFailure, + getResourceOriginalResumeByIdSuccess, +} from '.'; +import { OnboardingClientState } from '../onboardingClient.state'; + +@Injectable() +export class ResourceEffects { + constructor( + private readonly actions$: Actions, + private sharedStore: Store, + private authService: OnboardingClientAuthService, + private resourceService: OnboardingClientResourceService, + private viewsService: OnboardingClientViewsService, + ) {} + + getResourceInformationById$ = createEffect(() => + this.actions$.pipe( + ofType(getResourceInformationById), + switchMap(action => + this.resourceService.getResourceInformationById(action.resourceId).pipe( + map(data => { + return getResourceInformationByIdSuccess({ + firstName: data.firstName, + lastName: data.lastName, + email: data.email, + phoneNumber: data.phoneNumber, + city: data.location.city, + province: data.location.province, + country: data.location.country, + linkedInLink: data.linkedInLink, + githubLink: data.githubLink, + otherLink: data.otherLink, + projectResources: data.projectResources, + }); + }), + catchError(error => of(getResourceInformationByIdFailure({ error }))), + ), + ), + ), + ); + + updateInfo$ = createEffect(() => + this.actions$.pipe( + ofType(updateUserInfo), + switchMap(action => + this.resourceService.editResourcePersonalInformation(action.updatedPersonalInformation).pipe( + map(data => { + return updateUserInfoSuccess({ + firstName: data.firstName, + lastName: data.lastName, + email: data.email, + }); + }), + catchError(error => of(updateInfoFailure({ error }))), + ), + ), + ), + ); + + getAllViews$ = createEffect(() => + this.actions$.pipe( + ofType(getAllViewsByResourceId), + switchMap(data => + this.viewsService.getViewsByResourceId(data.resourceId, data.pageNum, data.pageSize).pipe( + map(res => { + return getAllViewsByResourceIdSuccess({ views: res.views, totalViews: res.totalViews }); + }), + catchError(error => of(getAllViewsByResourceIdFailure({ error }))), + ), + ), + ), + ); + + getResourceOriginalResume$ = createEffect(() => + this.actions$.pipe( + ofType(getResourceOriginalResumeById), + switchMap(data => + this.resourceService.getResourceOriginalResumeById(data.resourceId).pipe( + map(res => { + return getResourceOriginalResumeByIdSuccess({ resume: res }); + }), + catchError(error => of(getAllViewsByResourceIdFailure({ error }))), + ), + ), + ), + ); + + downloadProfile$ = createEffect(() => + this.actions$.pipe( + ofType(downloadProfileByViewId), + switchMap(data => + this.resourceService.downloadProfile(data.viewId).pipe( + map(res => { + return downloadProfileByViewIdSuccess({ resume: res }); + }), + catchError(error => of(downloadProfileByViewIdFailure({ error }))), + ), + ), + ), + ); +} diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.reducers.ts b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.reducers.ts new file mode 100644 index 00000000..426c217a --- /dev/null +++ b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.reducers.ts @@ -0,0 +1,135 @@ +import { createReducer, on } from '@ngrx/store'; +import { ProjectResource, View } from '@tempus/shared-domain'; +import { AsyncRequestState } from '../../enum'; +import * as ResourceActions from './resource.actions'; + +export const RESOURCE_INFO_FEATURE_KEY = 'resource'; + +export interface ResourceState { + firstName: string | null; + lastName: string | null; + email: string | null; + phoneNumber: string | null; + views: View[] | null; + resume: Blob | null; + totalViewsData: number; + city: string | null; + province: string | null; + country: string | null; + linkedInLink: string | null; + githubLink: string | null; + otherLink: string | null; + projectResources: ProjectResource[] | null; + error: Error | null; +} + +export const initialState: ResourceState = { + firstName: null, + lastName: null, + email: null, + phoneNumber: null, + views: null, + resume: null, + totalViewsData: 0, + city: null, + province: null, + country: null, + linkedInLink: null, + githubLink: null, + otherLink: null, + projectResources: null, + error: null, +}; + +export const resourceReducer = createReducer( + initialState, + // getResourceInformationById + on(ResourceActions.getResourceInformationById, state => ({ ...state, status: AsyncRequestState.LOADING })), + on( + ResourceActions.getResourceInformationByIdSuccess, + ( + state, + { + firstName, + lastName, + email, + city, + province, + country, + phoneNumber, + linkedInLink, + githubLink, + otherLink, + projectResources, + }, + ) => ({ + ...state, + firstName, + lastName, + email, + city, + province, + country, + phoneNumber, + linkedInLink, + githubLink, + otherLink, + projectResources, + }), + ), + on(ResourceActions.getResourceInformationByIdFailure, (state, { error }) => ({ + ...state, + status: AsyncRequestState.ERROR, + error, + })), + + // updateUserInformation + on(ResourceActions.updateUserInfo, state => ({ ...state, status: AsyncRequestState.LOADING })), + on(ResourceActions.updateUserInfoSuccess, (state, { firstName, lastName, email }) => ({ + ...state, + firstName, + lastName, + email, + })), + on(ResourceActions.updateInfoFailure, (state, { error }) => ({ + ...state, + status: AsyncRequestState.ERROR, + error, + })), + // getAllViewsByResourceId + on(ResourceActions.getAllViewsByResourceId, state => ({ ...state, status: AsyncRequestState.LOADING })), + on(ResourceActions.getAllViewsByResourceIdSuccess, (state, { views, totalViews }) => ({ + ...state, + views, + totalViewsData: totalViews, + status: AsyncRequestState.SUCCESS, + error: null, + })), + on(ResourceActions.getAllViewsByResourceIdFailure, (state, { error }) => ({ + ...state, + error, + status: AsyncRequestState.ERROR, + })), + // getResourceOriginalResumeById + on(ResourceActions.getResourceOriginalResumeById, state => ({ ...state, status: AsyncRequestState.LOADING })), + on(ResourceActions.getResourceOriginalResumeByIdSuccess, (state, { resume }) => ({ + ...state, + resume, + status: AsyncRequestState.SUCCESS, + })), + on(ResourceActions.getResourceOriginalResumeByIdFailure, (state, { error }) => ({ + ...state, + error, + })), + // downloadProfileByViewId + on(ResourceActions.downloadProfileByViewId, state => ({ ...state, status: AsyncRequestState.LOADING })), + on(ResourceActions.downloadProfileByViewIdSuccess, (state, { resume }) => ({ + ...state, + resume, + status: AsyncRequestState.SUCCESS, + })), + on(ResourceActions.downloadProfileByViewIdFailure, (state, { error }) => ({ + ...state, + error, + })), +); diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.selectors.ts b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.selectors.ts new file mode 100644 index 00000000..cced639f --- /dev/null +++ b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.selectors.ts @@ -0,0 +1,37 @@ +import { createSelector } from '@ngrx/store'; +import { OnboardingClientState, selectOnboardingClientState } from '../onboardingClient.state'; + +import { ResourceState, RESOURCE_INFO_FEATURE_KEY } from './resource.reducers'; + +export const selectState = createSelector( + selectOnboardingClientState, + (state: OnboardingClientState) => state[RESOURCE_INFO_FEATURE_KEY], +); + +export const selectResourceDetails = createSelector(selectState, (state: ResourceState) => { + return { + firstName: state.firstName, + lastName: state.lastName, + email: state.email, + phoneNumber: state.phoneNumber, + city: state.city, + province: state.province, + country: state.country, + linkedInLink: state.linkedInLink, + githubLink: state.githubLink, + otherLink: state.otherLink, + projectResources: state.projectResources, + }; +}); + +export const selectResourceBasicDetails = createSelector(selectState, (state: ResourceState) => { + return { firstName: state.firstName, lastName: state.lastName, email: state.email }; +}); + +export const selectResourceViews = createSelector(selectState, (state: ResourceState) => { + return { views: state.views, totalViews: state.totalViewsData }; +}); + +export const selectResourceOriginalResume = createSelector(selectState, (state: ResourceState) => state.resume); + +export const selectDownloadProfile = createSelector(selectState, (state: ResourceState) => state.resume); diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/onboarding-client-shared-data-access.module.ts b/libs/client/onboarding-client/shared/data-access/src/lib/onboarding-client-shared-data-access.module.ts index e4bd940f..669e5c9d 100644 --- a/libs/client/onboarding-client/shared/data-access/src/lib/onboarding-client-shared-data-access.module.ts +++ b/libs/client/onboarding-client/shared/data-access/src/lib/onboarding-client-shared-data-access.module.ts @@ -2,7 +2,7 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { StoreModule } from '@ngrx/store'; import { EffectsModule } from '@ngrx/effects'; -import { ONBOARDING_CLIENT_FEATURE_KEY, reducers } from './+state'; +import { ONBOARDING_CLIENT_FEATURE_KEY, reducers, ResourceEffects } from './+state'; import { hydrationMetaReducer } from './+state/hydration.reducer'; import { AuthEffects } from './+state/auth/auth.effects'; @@ -10,7 +10,7 @@ import { AuthEffects } from './+state/auth/auth.effects'; imports: [ CommonModule, StoreModule.forFeature(ONBOARDING_CLIENT_FEATURE_KEY, reducers, { metaReducers: [hydrationMetaReducer] }), - EffectsModule.forFeature([AuthEffects]), + EffectsModule.forFeature([AuthEffects, ResourceEffects]), ], }) export class OnboardingClientSharedDataAccessModule {} From 402ae8b55bf27065777adb44015337a82187db2d Mon Sep 17 00:00:00 2001 From: GCham5 Date: Thu, 17 Nov 2022 14:23:26 -0500 Subject: [PATCH 2/7] feat: remove userId from session storage --- libs/api/shared/feature-auth/src/lib/auth.service.ts | 1 + .../data-access/src/lib/+state/auth/auth.actions.ts | 1 - .../data-access/src/lib/+state/auth/auth.effects.ts | 5 ++--- .../data-access/src/lib/+state/auth/auth.reducers.ts | 5 +---- .../data-access/src/lib/+state/auth/auth.selectors.ts | 2 +- .../data-access/src/lib/+state/hydration.reducer.ts | 3 --- .../src/lib/services/onboarding-client-auth.service.ts | 8 -------- 7 files changed, 5 insertions(+), 20 deletions(-) diff --git a/libs/api/shared/feature-auth/src/lib/auth.service.ts b/libs/api/shared/feature-auth/src/lib/auth.service.ts index b8c9308e..4dc5aa27 100644 --- a/libs/api/shared/feature-auth/src/lib/auth.service.ts +++ b/libs/api/shared/feature-auth/src/lib/auth.service.ts @@ -24,6 +24,7 @@ export class AuthService { const tokens = await this.createTokens(user); await this.updateRefreshTokenHash(user, tokens.refreshToken); const partialUser: UserEntity = { ...user }; + partialUser.id = null; partialUser.password = null; partialUser.refreshToken = null; const result = new AuthDto(partialUser, tokens.accessToken, tokens.refreshToken); diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/+state/auth/auth.actions.ts b/libs/client/onboarding-client/shared/data-access/src/lib/+state/auth/auth.actions.ts index fa6e802a..8b65f9bb 100644 --- a/libs/client/onboarding-client/shared/data-access/src/lib/+state/auth/auth.actions.ts +++ b/libs/client/onboarding-client/shared/data-access/src/lib/+state/auth/auth.actions.ts @@ -9,7 +9,6 @@ export const loginSuccess = createAction( props<{ accessToken: string; refreshToken: string; - loggedInUserId: number; firstName: string; lastName: string; email: string; diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/+state/auth/auth.effects.ts b/libs/client/onboarding-client/shared/data-access/src/lib/+state/auth/auth.effects.ts index 29e6f677..fe9e23f8 100644 --- a/libs/client/onboarding-client/shared/data-access/src/lib/+state/auth/auth.effects.ts +++ b/libs/client/onboarding-client/shared/data-access/src/lib/+state/auth/auth.effects.ts @@ -1,6 +1,6 @@ import { Actions, createEffect, ofType } from '@ngrx/effects'; import { of, switchMap } from 'rxjs'; -import { map, catchError, tap } from 'rxjs/operators'; +import { map, catchError } from 'rxjs/operators'; import { Store } from '@ngrx/store'; import { Injectable } from '@angular/core'; import { Router } from '@angular/router'; @@ -27,7 +27,6 @@ export class AuthEffects { return loginSuccess({ accessToken: data.accessToken, refreshToken: data.refreshToken, - loggedInUserId: data.user.id, firstName: data.user.firstName, lastName: data.user.lastName, email: data.user.email, @@ -44,7 +43,7 @@ export class AuthEffects { ofType(logout), switchMap(options => this.authService.logout().pipe( - map(_ => { + map(() => { this.authService.resetSessionStorage(); if (options.redirect) { this.router.navigateByUrl('/signin'); diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/+state/auth/auth.reducers.ts b/libs/client/onboarding-client/shared/data-access/src/lib/+state/auth/auth.reducers.ts index b9b70d0b..4ce65a03 100644 --- a/libs/client/onboarding-client/shared/data-access/src/lib/+state/auth/auth.reducers.ts +++ b/libs/client/onboarding-client/shared/data-access/src/lib/+state/auth/auth.reducers.ts @@ -8,7 +8,6 @@ export const AUTH_FEATURE_KEY = 'auth'; export interface AuthState { accessToken: string | null; refreshToken: string | null; - loggedInUserId: number | null; firstName: string | null; lastName: string | null; email: string | null; @@ -19,7 +18,6 @@ export interface AuthState { export const initialState: AuthState = { accessToken: null, refreshToken: null, - loggedInUserId: null, error: null, firstName: null, lastName: null, @@ -30,11 +28,10 @@ export const initialState: AuthState = { export const authReducer = createReducer( initialState, on(AuthActions.login, state => ({ ...state, status: AsyncRequestState.LOADING })), - on(AuthActions.loginSuccess, (state, { accessToken, refreshToken, loggedInUserId, firstName, lastName, email }) => ({ + on(AuthActions.loginSuccess, (state, { accessToken, refreshToken, firstName, lastName, email }) => ({ ...state, accessToken, refreshToken, - loggedInUserId, firstName, lastName, email, diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/+state/auth/auth.selectors.ts b/libs/client/onboarding-client/shared/data-access/src/lib/+state/auth/auth.selectors.ts index e322ba4a..650b7b05 100644 --- a/libs/client/onboarding-client/shared/data-access/src/lib/+state/auth/auth.selectors.ts +++ b/libs/client/onboarding-client/shared/data-access/src/lib/+state/auth/auth.selectors.ts @@ -12,7 +12,7 @@ export const selectAccessRefreshToken = createSelector(selectAuth, (state: AuthS accessToken: state.accessToken, refreshToken: state.refreshToken, })); -export const selectLoggedInUserId = createSelector(selectAuth, (state: AuthState) => state.loggedInUserId); + export const selectLoginStatus = createSelector(selectAuth, (state: AuthState) => { return { status: state.status, diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/+state/hydration.reducer.ts b/libs/client/onboarding-client/shared/data-access/src/lib/+state/hydration.reducer.ts index 15cceeb0..9f749565 100644 --- a/libs/client/onboarding-client/shared/data-access/src/lib/+state/hydration.reducer.ts +++ b/libs/client/onboarding-client/shared/data-access/src/lib/+state/hydration.reducer.ts @@ -10,8 +10,6 @@ export const hydrationMetaReducer = ( if (action.type === INIT || action.type === UPDATE || action.type === '@ngrx/router-store/navigated') { const accessToken: string | null = sessionStorage.getItem(SessionStorageKey.TEMPUS_ACCESS_TOKEN); const refreshToken: string | null = sessionStorage.getItem(SessionStorageKey.TEMPUS_REFRESH_TOKEN); - const userIdString: string | null = sessionStorage.getItem(SessionStorageKey.TEMPUS_USER_ID); - const userId: number | null = userIdString ? parseInt(userIdString, 10) : null; const firstName: string | null = sessionStorage.getItem(SessionStorageKey.TEMPUS_FIRST_NAME); const lastName: string | null = sessionStorage.getItem(SessionStorageKey.TEMPUS_LAST_NAME); const email: string | null = sessionStorage.getItem(SessionStorageKey.TEMPUS_EMAIL); @@ -23,7 +21,6 @@ export const hydrationMetaReducer = ( ...newState.auth, accessToken, refreshToken, - loggedInUserId: userId, firstName, lastName, email, diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/services/onboarding-client-auth.service.ts b/libs/client/onboarding-client/shared/data-access/src/lib/services/onboarding-client-auth.service.ts index d838b8c7..b6d046b3 100644 --- a/libs/client/onboarding-client/shared/data-access/src/lib/services/onboarding-client-auth.service.ts +++ b/libs/client/onboarding-client/shared/data-access/src/lib/services/onboarding-client-auth.service.ts @@ -19,7 +19,6 @@ export class OnboardingClientAuthService { this.setUserDataInSessionStorage( data.accessToken, data.refreshToken, - data.user.id, data.user.firstName, data.user.lastName, data.user.email, @@ -42,15 +41,12 @@ export class OnboardingClientAuthService { public getUserDataFromSessionStorage(): { accessToken: string | null; refreshToken: string | null; - userId: number | null; email: string | null; firstName: string | null; lastName: string | null; } { const accessToken: string | null = sessionStorage.getItem(SessionStorageKey.TEMPUS_ACCESS_TOKEN); const refreshToken: string | null = sessionStorage.getItem(SessionStorageKey.TEMPUS_REFRESH_TOKEN); - const userIdString: string | null = sessionStorage.getItem(SessionStorageKey.TEMPUS_USER_ID); - const userId: number | null = userIdString ? parseInt(userIdString, 10) : null; const firstName: string | null = sessionStorage.getItem(SessionStorageKey.TEMPUS_FIRST_NAME); const lastName: string | null = sessionStorage.getItem(SessionStorageKey.TEMPUS_LAST_NAME); const email: string | null = sessionStorage.getItem(SessionStorageKey.TEMPUS_EMAIL); @@ -58,7 +54,6 @@ export class OnboardingClientAuthService { return { accessToken, refreshToken, - userId, firstName, lastName, email, @@ -73,14 +68,12 @@ export class OnboardingClientAuthService { public setUserDataInSessionStorage( accessToken: string, refreshToken: string, - userId: number, firstName: string, lastName: string, email: string, ) { sessionStorage.setItem(SessionStorageKey.TEMPUS_ACCESS_TOKEN, accessToken); sessionStorage.setItem(SessionStorageKey.TEMPUS_REFRESH_TOKEN, refreshToken); - sessionStorage.setItem(SessionStorageKey.TEMPUS_USER_ID, userId.toString()); sessionStorage.setItem(SessionStorageKey.TEMPUS_FIRST_NAME, firstName); sessionStorage.setItem(SessionStorageKey.TEMPUS_LAST_NAME, lastName); sessionStorage.setItem(SessionStorageKey.TEMPUS_EMAIL, email); @@ -92,7 +85,6 @@ export class OnboardingClientAuthService { public resetSessionStorage() { sessionStorage.removeItem(SessionStorageKey.TEMPUS_ACCESS_TOKEN); - sessionStorage.removeItem(SessionStorageKey.TEMPUS_USER_ID); sessionStorage.removeItem(SessionStorageKey.TEMPUS_FIRST_NAME); sessionStorage.removeItem(SessionStorageKey.TEMPUS_LAST_NAME); sessionStorage.removeItem(SessionStorageKey.TEMPUS_EMAIL); From 453d240f856283d68e9021fd237e2796f8d54d1c Mon Sep 17 00:00:00 2001 From: GCham5 Date: Thu, 17 Nov 2022 14:24:43 -0500 Subject: [PATCH 3/7] feat(frontend): add getResourceInformation to store --- .../personal-information-display.component.ts | 74 +++++++++++++------ .../src/lib/profile/profile.component.ts | 4 +- .../src/lib/my-views/my-views.component.ts | 4 +- .../lib/+state/resource/resource.actions.ts | 25 +++++++ .../lib/+state/resource/resource.effects.ts | 30 ++++++++ .../lib/+state/resource/resource.reducers.ts | 43 +++++++++++ .../lib/+state/resource/resource.selectors.ts | 2 + 7 files changed, 157 insertions(+), 25 deletions(-) diff --git a/libs/client/onboarding-client/resource/features/feature-personal-information/src/lib/personal-information-display/personal-information-display.component.ts b/libs/client/onboarding-client/resource/features/feature-personal-information/src/lib/personal-information-display/personal-information-display.component.ts index a119f564..2f995ff9 100644 --- a/libs/client/onboarding-client/resource/features/feature-personal-information/src/lib/personal-information-display/personal-information-display.component.ts +++ b/libs/client/onboarding-client/resource/features/feature-personal-information/src/lib/personal-information-display/personal-information-display.component.ts @@ -6,7 +6,16 @@ import { ButtonType } from '@tempus/client/shared/ui-components/presentational'; import { FormBuilder, FormGroup } from '@angular/forms'; import { ICreateLocationDto, IUpdateResourceDto } from '@tempus/shared-domain'; import { TempusResourceState, updateUserInfo } from '@tempus/client/onboarding-client/resource/data-access'; -import { OnboardingClientResourceService } from '@tempus/client/onboarding-client/shared/data-access'; +import { + getResourceInformation, + getResourceOriginalResumeById, + OnboardingClientResourceService, + OnboardingClientState, + selectResourceDetails, + selectResourceId, + selectResourceOriginalResume, +} from '@tempus/client/onboarding-client/shared/data-access'; +import { Subject, takeUntil } from 'rxjs'; @Component({ selector: 'tempus-personal-information-display', @@ -14,6 +23,8 @@ import { OnboardingClientResourceService } from '@tempus/client/onboarding-clien styleUrls: ['./personal-information-display.component.scss'], }) export class PersonalInformationDisplayComponent implements OnInit { + $destroyed = new Subject(); + UserType = UserType; userId = 0; @@ -55,6 +66,7 @@ export class PersonalInformationDisplayComponent implements OnInit { private translateService: TranslateService, private fb: FormBuilder, private store: Store, + private sharedStore: Store, ) { const { currentLang } = translateService; // eslint-disable-next-line no-param-reassign @@ -63,26 +75,46 @@ export class PersonalInformationDisplayComponent implements OnInit { } ngOnInit(): void { - this.resourceService.getResourceInformation().subscribe(resData => { - this.userId = resData.id; - this.firstName = resData.firstName; - this.lastName = resData.lastName; - this.email = resData.email; - this.city = resData.location.city; - this.state = resData.location.province; - this.country = resData.location.country; - this.phoneNumber = resData.phoneNumber; - this.email = resData.email; - this.phoneNumber = resData.phoneNumber; - this.linkedInLink = resData.linkedInLink; - this.githubLink = resData.githubLink; - this.otherLink = resData.otherLink; - this.fullName = `${resData.firstName} ${resData.lastName}`; - }); - - this.resourceService.getResourceOriginalResumeById(this.userId).subscribe(resumeBlob => { - this.resume = new File([resumeBlob], 'original-resume.pdf'); - }); + this.sharedStore.dispatch(getResourceInformation()); + + this.sharedStore + .select(selectResourceId) + .pipe(takeUntil(this.$destroyed)) + .subscribe(data => { + this.userId = data; + }); + + this.sharedStore + .select(selectResourceDetails) + .pipe(takeUntil(this.$destroyed)) + .subscribe(data => { + this.firstName = data.firstName || ''; + this.lastName = data.lastName || ''; + this.city = data.city || ''; + this.state = data.province || ''; + this.country = data.country || ''; + this.phoneNumber = data.phoneNumber || ''; + this.email = data.email || ''; + this.linkedInLink = data.linkedInLink || ''; + this.githubLink = data.githubLink || ''; + this.otherLink = data.otherLink || ''; + this.fullName = `${data.firstName} ${data.lastName}`; + }); + + this.sharedStore.dispatch(getResourceOriginalResumeById({ resourceId: this.userId })); + + this.sharedStore + .select(selectResourceOriginalResume) + .pipe(takeUntil(this.$destroyed)) + .subscribe(blob => { + if (blob) { + this.resume = new File([blob], 'original-resume.pdf'); + } + }); + + // this.resourceService.getResourceOriginalResumeById(this.userId).subscribe(resumeBlob => { + // this.resume = new File([resumeBlob], 'original-resume.pdf'); + // }); } loadPersonalInfo(eventData: FormGroup) { diff --git a/libs/client/onboarding-client/resource/features/feature-profile/src/lib/profile/profile.component.ts b/libs/client/onboarding-client/resource/features/feature-profile/src/lib/profile/profile.component.ts index 86a0b811..cf21d65d 100644 --- a/libs/client/onboarding-client/resource/features/feature-profile/src/lib/profile/profile.component.ts +++ b/libs/client/onboarding-client/resource/features/feature-profile/src/lib/profile/profile.component.ts @@ -2,8 +2,8 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { OnboardingClientResourceService, OnboardingClientState, - selectLoggedInUserId, selectLoggedInUserNameEmail, + selectResourceId, } from '@tempus/client/onboarding-client/shared/data-access'; import { Subject, take, takeUntil } from 'rxjs'; import { ButtonType } from '@tempus/client/shared/ui-components/presentational'; @@ -106,7 +106,7 @@ export class ProfileComponent implements OnInit, OnDestroy { ngOnInit(): void { this.sharedStore - .select(selectLoggedInUserId) + .select(selectResourceId) .pipe(take(1)) .subscribe(data => { if (data) { diff --git a/libs/client/onboarding-client/resource/features/feature-views/src/lib/my-views/my-views.component.ts b/libs/client/onboarding-client/resource/features/feature-views/src/lib/my-views/my-views.component.ts index 7f5471cc..8fec901d 100644 --- a/libs/client/onboarding-client/resource/features/feature-views/src/lib/my-views/my-views.component.ts +++ b/libs/client/onboarding-client/resource/features/feature-views/src/lib/my-views/my-views.component.ts @@ -6,8 +6,8 @@ import { TranslateService } from '@ngx-translate/core'; import { OnboardingClientResourceService, OnboardingClientState, - selectLoggedInUserId, selectLoggedInUserNameEmail, + selectResourceId, } from '@tempus/client/onboarding-client/shared/data-access'; import { ButtonType, Column, MyViewsTableData } from '@tempus/client/shared/ui-components/presentational'; import { Subject, take, takeUntil } from 'rxjs'; @@ -92,7 +92,7 @@ export class MyViewsComponent implements OnInit, OnDestroy { ngOnInit(): void { this.sharedStore - .select(selectLoggedInUserId) + .select(selectResourceId) .pipe(take(1)) .subscribe(data => { if (data) { diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.actions.ts b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.actions.ts index d816a62a..802e9be0 100644 --- a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.actions.ts +++ b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.actions.ts @@ -1,6 +1,31 @@ import { createAction, props } from '@ngrx/store'; import { IUpdateResourceDto, ProjectResource, View } from '@tempus/shared-domain'; +export const getResourceInformation = createAction('[Onboarding Client User Api] Get Resource Information'); + +export const getResourceInformationSuccess = createAction( + '[Onboarding Client User Api] Get Resource Information Success', + props<{ + userId: number; + firstName: string; + lastName: string; + email: string; + phoneNumber: string; + city: string; + province: string; + country: string; + linkedInLink: string; + githubLink: string; + otherLink: string; + projectResources: ProjectResource[]; + }>(), +); + +export const getResourceInformationFailure = createAction( + '[Onboarding Client User Api] Get Resource Information Failure', + props<{ error: Error }>(), +); + export const getResourceInformationById = createAction( '[Onboarding Client User Api] Get Resource Information By Id', props<{ resourceId: number }>(), diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.effects.ts b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.effects.ts index a5a8c0dc..17c3a1f4 100644 --- a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.effects.ts +++ b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.effects.ts @@ -10,8 +10,10 @@ import { } from '../../services'; import { getAllViewsByResourceId, + getResourceInformation, getResourceInformationById, getResourceInformationByIdSuccess, + getResourceInformationFailure, getResourceOriginalResumeById, updateInfoFailure, updateUserInfo, @@ -24,6 +26,7 @@ import { getAllViewsByResourceIdFailure, getAllViewsByResourceIdSuccess, getResourceInformationByIdFailure, + getResourceInformationSuccess, getResourceOriginalResumeByIdSuccess, } from '.'; import { OnboardingClientState } from '../onboardingClient.state'; @@ -38,6 +41,33 @@ export class ResourceEffects { private viewsService: OnboardingClientViewsService, ) {} + getResourceInformation$ = createEffect(() => + this.actions$.pipe( + ofType(getResourceInformation), + switchMap(() => + this.resourceService.getResourceInformation().pipe( + map(data => { + return getResourceInformationSuccess({ + userId: data.id, + firstName: data.firstName, + lastName: data.lastName, + email: data.email, + phoneNumber: data.phoneNumber, + city: data.location.city, + province: data.location.province, + country: data.location.country, + linkedInLink: data.linkedInLink, + githubLink: data.githubLink, + otherLink: data.otherLink, + projectResources: data.projectResources, + }); + }), + catchError(error => of(getResourceInformationFailure({ error }))), + ), + ), + ), + ); + getResourceInformationById$ = createEffect(() => this.actions$.pipe( ofType(getResourceInformationById), diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.reducers.ts b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.reducers.ts index 426c217a..3cd30d88 100644 --- a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.reducers.ts +++ b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.reducers.ts @@ -6,6 +6,7 @@ import * as ResourceActions from './resource.actions'; export const RESOURCE_INFO_FEATURE_KEY = 'resource'; export interface ResourceState { + userId: number; firstName: string | null; lastName: string | null; email: string | null; @@ -24,6 +25,7 @@ export interface ResourceState { } export const initialState: ResourceState = { + userId: 0, firstName: null, lastName: null, email: null, @@ -43,6 +45,47 @@ export const initialState: ResourceState = { export const resourceReducer = createReducer( initialState, + // getResourceInformation + on(ResourceActions.getResourceInformation, state => ({ ...state, status: AsyncRequestState.LOADING })), + on( + ResourceActions.getResourceInformationSuccess, + ( + state, + { + userId, + firstName, + lastName, + email, + city, + province, + country, + phoneNumber, + linkedInLink, + githubLink, + otherLink, + projectResources, + }, + ) => ({ + ...state, + userId, + firstName, + lastName, + email, + city, + province, + country, + phoneNumber, + linkedInLink, + githubLink, + otherLink, + projectResources, + }), + ), + on(ResourceActions.getResourceInformationFailure, (state, { error }) => ({ + ...state, + status: AsyncRequestState.ERROR, + error, + })), // getResourceInformationById on(ResourceActions.getResourceInformationById, state => ({ ...state, status: AsyncRequestState.LOADING })), on( diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.selectors.ts b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.selectors.ts index cced639f..8b1ab7c1 100644 --- a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.selectors.ts +++ b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.selectors.ts @@ -8,6 +8,8 @@ export const selectState = createSelector( (state: OnboardingClientState) => state[RESOURCE_INFO_FEATURE_KEY], ); +export const selectResourceId = createSelector(selectState, (state: ResourceState) => state.userId); + export const selectResourceDetails = createSelector(selectState, (state: ResourceState) => { return { firstName: state.firstName, From 300dc143e8f164fbbd9cbc37d6a65a490b80e5a3 Mon Sep 17 00:00:00 2001 From: GCham5 Date: Mon, 21 Nov 2022 19:20:22 -0500 Subject: [PATCH 4/7] chore: clean up --- .../lib/+state/resource/resource.reducers.ts | 44 ++++----- .../personal-information-display.component.ts | 9 +- .../src/lib/profile/profile.component.ts | 93 ++++++++++++------- .../src/lib/my-views/my-views.component.ts | 14 +-- .../lib/+state/resource/resource.selectors.ts | 3 +- 5 files changed, 84 insertions(+), 79 deletions(-) diff --git a/libs/client/onboarding-client/resource/data-access/src/lib/+state/resource/resource.reducers.ts b/libs/client/onboarding-client/resource/data-access/src/lib/+state/resource/resource.reducers.ts index 650da683..bf37ed61 100644 --- a/libs/client/onboarding-client/resource/data-access/src/lib/+state/resource/resource.reducers.ts +++ b/libs/client/onboarding-client/resource/data-access/src/lib/+state/resource/resource.reducers.ts @@ -10,7 +10,7 @@ export interface ResourceState { lastName: string | null; email: string | null; views: View[] | null; - resume: Blob | null; + // resume: Blob | null; totalViewsData: number; error: Error | null; } @@ -20,7 +20,7 @@ export const initialState: ResourceState = { lastName: null, email: null, views: null, - resume: null, + // resume: null, totalViewsData: 0, error: null, }; @@ -52,24 +52,24 @@ export const resourceReducer = createReducer( error, status: AsyncRequestState.ERROR, })), - on(ResourceActions.getResourceOriginalResumeById, state => ({ ...state, status: AsyncRequestState.LOADING })), - on(ResourceActions.getResourceOriginalResumeByIdSuccess, (state, { resume }) => ({ - ...state, - resume, - status: AsyncRequestState.SUCCESS, - })), - on(ResourceActions.getResourceOriginalResumeByIdFailure, (state, { error }) => ({ - ...state, - error, - })), - on(ResourceActions.downloadProfileByViewId, state => ({ ...state, status: AsyncRequestState.LOADING })), - on(ResourceActions.downloadProfileByViewIdSuccess, (state, { resume }) => ({ - ...state, - resume, - status: AsyncRequestState.SUCCESS, - })), - on(ResourceActions.downloadProfileByViewIdFailure, (state, { error }) => ({ - ...state, - error, - })), + // on(ResourceActions.getResourceOriginalResumeById, state => ({ ...state, status: AsyncRequestState.LOADING })), + // on(ResourceActions.getResourceOriginalResumeByIdSuccess, (state, { resume }) => ({ + // ...state, + // resume, + // status: AsyncRequestState.SUCCESS, + // })), + // on(ResourceActions.getResourceOriginalResumeByIdFailure, (state, { error }) => ({ + // ...state, + // error, + // })), + // on(ResourceActions.downloadProfileByViewId, state => ({ ...state, status: AsyncRequestState.LOADING })), + // on(ResourceActions.downloadProfileByViewIdSuccess, (state, { resume }) => ({ + // ...state, + // resume, + // status: AsyncRequestState.SUCCESS, + // })), + // on(ResourceActions.downloadProfileByViewIdFailure, (state, { error }) => ({ + // ...state, + // error, + // })), ); diff --git a/libs/client/onboarding-client/resource/features/feature-personal-information/src/lib/personal-information-display/personal-information-display.component.ts b/libs/client/onboarding-client/resource/features/feature-personal-information/src/lib/personal-information-display/personal-information-display.component.ts index 2f995ff9..772ecfcd 100644 --- a/libs/client/onboarding-client/resource/features/feature-personal-information/src/lib/personal-information-display/personal-information-display.component.ts +++ b/libs/client/onboarding-client/resource/features/feature-personal-information/src/lib/personal-information-display/personal-information-display.component.ts @@ -12,7 +12,6 @@ import { OnboardingClientResourceService, OnboardingClientState, selectResourceDetails, - selectResourceId, selectResourceOriginalResume, } from '@tempus/client/onboarding-client/shared/data-access'; import { Subject, takeUntil } from 'rxjs'; @@ -77,17 +76,11 @@ export class PersonalInformationDisplayComponent implements OnInit { ngOnInit(): void { this.sharedStore.dispatch(getResourceInformation()); - this.sharedStore - .select(selectResourceId) - .pipe(takeUntil(this.$destroyed)) - .subscribe(data => { - this.userId = data; - }); - this.sharedStore .select(selectResourceDetails) .pipe(takeUntil(this.$destroyed)) .subscribe(data => { + this.userId = data.userId; this.firstName = data.firstName || ''; this.lastName = data.lastName || ''; this.city = data.city || ''; diff --git a/libs/client/onboarding-client/resource/features/feature-profile/src/lib/profile/profile.component.ts b/libs/client/onboarding-client/resource/features/feature-profile/src/lib/profile/profile.component.ts index cf21d65d..ebeae6f4 100644 --- a/libs/client/onboarding-client/resource/features/feature-profile/src/lib/profile/profile.component.ts +++ b/libs/client/onboarding-client/resource/features/feature-profile/src/lib/profile/profile.component.ts @@ -1,9 +1,9 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { + getResourceInformation, OnboardingClientResourceService, OnboardingClientState, - selectLoggedInUserNameEmail, - selectResourceId, + selectResourceDetails, } from '@tempus/client/onboarding-client/shared/data-access'; import { Subject, take, takeUntil } from 'rxjs'; import { ButtonType } from '@tempus/client/shared/ui-components/presentational'; @@ -106,22 +106,17 @@ export class ProfileComponent implements OnInit, OnDestroy { ngOnInit(): void { this.sharedStore - .select(selectResourceId) + .select(selectResourceDetails) .pipe(take(1)) .subscribe(data => { if (data) { - this.userId = data; + this.userId = data.userId; + this.firstName = data.firstName || ''; + this.lastName = data.lastName || ''; + } else { + this.sharedStore.dispatch(getResourceInformation()); } }); - this.sharedStore - .select(selectLoggedInUserNameEmail) - .pipe(take(1)) - .subscribe(data => { - this.firstName = data.firstName || ''; - this.lastName = data.lastName || ''; - }); - - this.resourceStore.dispatch(getResourceOriginalResumeById({ resourceId: this.userId })); this.resourceStore .select(selectResourceOriginalResume) @@ -129,6 +124,8 @@ export class ProfileComponent implements OnInit, OnDestroy { .subscribe(blob => { if (blob) { this.resume = new File([blob], 'original-resume.pdf'); + } else { + this.resourceStore.dispatch(getResourceOriginalResumeById({ resourceId: this.userId })); } }); @@ -162,34 +159,58 @@ export class ProfileComponent implements OnInit, OnDestroy { // this.dataLoaded = true; // }); + const viewId = parseInt(this.route.snapshot.paramMap.get('id') || '0', 10); + if (viewId) { + this.resourceService.getViewById(viewId).subscribe(view => { + this.pageTitle = view.type; + this.loadView(view); + this.dataLoaded = true; + }); + } else { + // Display Primary view + this.translateService + .get('onboardingResourceProfile.myProfile') + .pipe(take(1)) + .subscribe(data => { + this.pageTitle = data; + }); + + this.resourceService.getResourceProfileViews(this.userId).subscribe(data => { + let filteredAndSortedViews = data.views?.filter(view => view.viewType === ViewType.PRIMARY); + filteredAndSortedViews = sortViewsByLatestUpdated(filteredAndSortedViews); + this.loadView(filteredAndSortedViews[0]); + this.dataLoaded = true; + }); + } + this.resourceService.getResourceInformation().subscribe(resData => { this.userId = resData.id; this.fullName = `${resData.firstName} ${resData.lastName}`; // Use viewId from route - const viewId = parseInt(this.route.snapshot.paramMap.get('id') || '0', 10); - if (viewId) { - this.resourceService.getViewById(viewId).subscribe(view => { - this.pageTitle = view.type; - this.loadView(view); - this.dataLoaded = true; - }); - } else { - // Display Primary view - this.translateService - .get('onboardingResourceProfile.myProfile') - .pipe(take(1)) - .subscribe(data => { - this.pageTitle = data; - }); - - this.resourceService.getResourceProfileViews(this.userId).subscribe(data => { - let filteredAndSortedViews = data.views?.filter(view => view.viewType === ViewType.PRIMARY); - filteredAndSortedViews = sortViewsByLatestUpdated(filteredAndSortedViews); - this.loadView(filteredAndSortedViews[0]); - this.dataLoaded = true; - }); - } + // const viewId = parseInt(this.route.snapshot.paramMap.get('id') || '0', 10); + // if (viewId) { + // this.resourceService.getViewById(viewId).subscribe(view => { + // this.pageTitle = view.type; + // this.loadView(view); + // this.dataLoaded = true; + // }); + // } else { + // // Display Primary view + // this.translateService + // .get('onboardingResourceProfile.myProfile') + // .pipe(take(1)) + // .subscribe(data => { + // this.pageTitle = data; + // }); + + // this.resourceService.getResourceProfileViews(this.userId).subscribe(data => { + // let filteredAndSortedViews = data.views?.filter(view => view.viewType === ViewType.PRIMARY); + // filteredAndSortedViews = sortViewsByLatestUpdated(filteredAndSortedViews); + // this.loadView(filteredAndSortedViews[0]); + // this.dataLoaded = true; + // }); + // } }); } diff --git a/libs/client/onboarding-client/resource/features/feature-views/src/lib/my-views/my-views.component.ts b/libs/client/onboarding-client/resource/features/feature-views/src/lib/my-views/my-views.component.ts index 8fec901d..daeab2ef 100644 --- a/libs/client/onboarding-client/resource/features/feature-views/src/lib/my-views/my-views.component.ts +++ b/libs/client/onboarding-client/resource/features/feature-views/src/lib/my-views/my-views.component.ts @@ -6,8 +6,7 @@ import { TranslateService } from '@ngx-translate/core'; import { OnboardingClientResourceService, OnboardingClientState, - selectLoggedInUserNameEmail, - selectResourceId, + selectResourceDetails, } from '@tempus/client/onboarding-client/shared/data-access'; import { ButtonType, Column, MyViewsTableData } from '@tempus/client/shared/ui-components/presentational'; import { Subject, take, takeUntil } from 'rxjs'; @@ -92,17 +91,10 @@ export class MyViewsComponent implements OnInit, OnDestroy { ngOnInit(): void { this.sharedStore - .select(selectResourceId) - .pipe(take(1)) - .subscribe(data => { - if (data) { - this.userId = data; - } - }); - this.sharedStore - .select(selectLoggedInUserNameEmail) + .select(selectResourceDetails) .pipe(take(1)) .subscribe(data => { + this.userId = data.userId; this.firstName = data.firstName || ''; this.lastName = data.lastName || ''; }); diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.selectors.ts b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.selectors.ts index 8b1ab7c1..4c8bd422 100644 --- a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.selectors.ts +++ b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.selectors.ts @@ -8,10 +8,9 @@ export const selectState = createSelector( (state: OnboardingClientState) => state[RESOURCE_INFO_FEATURE_KEY], ); -export const selectResourceId = createSelector(selectState, (state: ResourceState) => state.userId); - export const selectResourceDetails = createSelector(selectState, (state: ResourceState) => { return { + userId: state.userId, firstName: state.firstName, lastName: state.lastName, email: state.email, From 45858e3aafd69ca7f37e5279085672670db92ff2 Mon Sep 17 00:00:00 2001 From: GCham5 Date: Mon, 5 Dec 2022 09:08:45 -0500 Subject: [PATCH 5/7] feat(frontend): implement shared store --- .../resource-profile-content.component.ts | 276 +++++++++++++----- .../resource-profile.component.ts | 46 ++- .../src/lib/user-bar/user-bar.component.ts | 41 ++- .../lib/+state/resource/resource.reducers.ts | 4 +- .../edit-profile/edit-profile.component.ts | 122 ++++++-- .../src/lib/profile/profile.component.ts | 140 +++++---- .../create-new-view.component.ts | 70 ++++- .../lib/+state/resource/resource.actions.ts | 67 ++++- .../lib/+state/resource/resource.effects.ts | 68 +++++ .../lib/+state/resource/resource.reducers.ts | 63 +++- .../lib/+state/resource/resource.selectors.ts | 6 + .../onboarding-client-resource.service.ts | 4 + .../onboarding-client-views.service.ts | 39 ++- 13 files changed, 733 insertions(+), 213 deletions(-) diff --git a/libs/client/onboarding-client/business-owner/features/feature-view-resource-profile/src/lib/resource-profile-content/resource-profile-content.component.ts b/libs/client/onboarding-client/business-owner/features/feature-view-resource-profile/src/lib/resource-profile-content/resource-profile-content.component.ts index a33d4490..a87faf82 100644 --- a/libs/client/onboarding-client/business-owner/features/feature-view-resource-profile/src/lib/resource-profile-content/resource-profile-content.component.ts +++ b/libs/client/onboarding-client/business-owner/features/feature-view-resource-profile/src/lib/resource-profile-content/resource-profile-content.component.ts @@ -21,13 +21,22 @@ import { ProjectResource, ViewType, } from '@tempus/shared-domain'; -import { OnboardingClientResourceService } from '@tempus/client/onboarding-client/shared/data-access'; +import { + approveOrDenyRevision, + getAllViewsByResourceId, + getViewById, + OnboardingClientResourceService, + OnboardingClientState, + selectResourceViews, + selectView, +} from '@tempus/client/onboarding-client/shared/data-access'; import { ModalService, CustomModalType, ModalType } from '@tempus/client/shared/ui-components/modal'; import { ActivatedRoute, Router } from '@angular/router'; import { FormBuilder, Validators } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; -import { take } from 'rxjs'; +import { Subject, take, takeUntil } from 'rxjs'; import { sortViewsByLatestUpdated } from '@tempus/client/shared/util'; +import { Store } from '@ngrx/store'; @Component({ selector: 'tempus-resource-profile-content', @@ -41,8 +50,8 @@ export class ResourceProfileContentComponent implements OnInit, OnChanges { private route: ActivatedRoute, private fb: FormBuilder, public modalService: ModalService, - private resourceService: OnboardingClientResourceService, private translateService: TranslateService, + private sharedStore: Store, ) { const { currentLang } = translateService; // eslint-disable-next-line no-param-reassign @@ -92,6 +101,8 @@ export class ResourceProfileContentComponent implements OnInit, OnChanges { @Input() projectResources: ProjectResource[] = []; + $destroyed = new Subject(); + experiencesSummary = ''; educationsSummary = ''; @@ -134,86 +145,173 @@ export class ResourceProfileContentComponent implements OnInit, OnChanges { ngOnInit() { const id = parseInt(this.route.snapshot.paramMap.get('id') || '0', 10); - this.resourceService.getResourceProfileViews(id).subscribe(data => { - this.viewID = data.views[0].id; - - // Load view that needs approval - const sortedViews = sortViewsByLatestUpdated(data.views); - let latestView = sortedViews[0]; - - // If no approvals, display approved Primary view - if (latestView.revisionType !== RevisionType.PENDING) { - const approvedViews = sortedViews.filter( - view => view.viewType === ViewType.PRIMARY && view.revisionType === RevisionType.APPROVED, - ); - const [view] = approvedViews; - latestView = view; - } - // if param is passed, we load the id - const currentViewId = parseInt(this.route.snapshot.queryParamMap.get('viewId') || '0', 10); - // if param is not passed, we load the latest id and set viewId to that param - if (!currentViewId) { - this.router.navigate([], { - relativeTo: this.route, - queryParams: { viewId: latestView.id }, - replaceUrl: true, - }); - } - - const set = new Set(); - const uniqueViewNames = sortedViews.filter(item => { - const viewExists = set.has(item.type); - // Filter out rejected views - if (item.revisionType === RevisionType.REJECTED) { - return false; + this.sharedStore.dispatch(getAllViewsByResourceId({ resourceId: id, pageNum: 0, pageSize: 1000 })); + this.sharedStore + .select(selectResourceViews) + .pipe(takeUntil(this.$destroyed)) + .subscribe(data => { + this.viewID = data?.views[0].id; + + // Load view that needs approval + const sortedViews = sortViewsByLatestUpdated(data?.views); + let latestView = sortedViews[0]; + + // If no approvals, display approved Primary view + if (latestView.revisionType !== RevisionType.PENDING) { + const approvedViews = sortedViews.filter( + view => view.viewType === ViewType.PRIMARY && view.revisionType === RevisionType.APPROVED, + ); + const [view] = approvedViews; + latestView = view; + } + // if param is passed, we load the id + const currentViewId = parseInt(this.route.snapshot.queryParamMap.get('viewId') || '0', 10); + // if param is not passed, we load the latest id and set viewId to that param + if (!currentViewId) { + this.router.navigate([], { + relativeTo: this.route, + queryParams: { viewId: latestView.id }, + replaceUrl: true, + }); } - // Filter out views that have been renamed - if (item.revision) { - const latestViews = sortViewsByLatestUpdated(item.revision.views); - if (latestViews[0].type !== item.type) { + const set = new Set(); + const uniqueViewNames = sortedViews.filter(item => { + const viewExists = set.has(item.type); + // Filter out rejected views + if (item.revisionType === RevisionType.REJECTED) { return false; } - } - set.add(item.type); - return !viewExists; + // Filter out views that have been renamed + if (item.revision) { + const latestViews = sortViewsByLatestUpdated(item.revision.views); + if (latestViews[0].type !== item.type) { + return false; + } + } + + set.add(item.type); + return !viewExists; + }); + this.loadView(currentViewId || latestView.id, uniqueViewNames); }); - this.loadView(currentViewId || latestView.id, uniqueViewNames); - }); + + // this.resourceService.getResourceProfileViews(id).subscribe(data => { + // this.viewID = data.views[0].id; + + // // Load view that needs approval + // const sortedViews = sortViewsByLatestUpdated(data.views); + // let latestView = sortedViews[0]; + + // // If no approvals, display approved Primary view + // if (latestView.revisionType !== RevisionType.PENDING) { + // const approvedViews = sortedViews.filter( + // view => view.viewType === ViewType.PRIMARY && view.revisionType === RevisionType.APPROVED, + // ); + // const [view] = approvedViews; + // latestView = view; + // } + // // if param is passed, we load the id + // const currentViewId = parseInt(this.route.snapshot.queryParamMap.get('viewId') || '0', 10); + // // if param is not passed, we load the latest id and set viewId to that param + // if (!currentViewId) { + // this.router.navigate([], { + // relativeTo: this.route, + // queryParams: { viewId: latestView.id }, + // replaceUrl: true, + // }); + // } + + // const set = new Set(); + // const uniqueViewNames = sortedViews.filter(item => { + // const viewExists = set.has(item.type); + // // Filter out rejected views + // if (item.revisionType === RevisionType.REJECTED) { + // return false; + // } + + // // Filter out views that have been renamed + // if (item.revision) { + // const latestViews = sortViewsByLatestUpdated(item.revision.views); + // if (latestViews[0].type !== item.type) { + // return false; + // } + // } + + // set.add(item.type); + // return !viewExists; + // }); + // this.loadView(currentViewId || latestView.id, uniqueViewNames); + // }); } loadView(viewID: number, profileViews?: ViewNames[]) { this.viewID = viewID; - this.resourceService.getViewById(viewID).subscribe(resourceView => { - this.certifications = resourceView.certifications; - this.educations = resourceView.educations; - this.educationsSummary = resourceView.educationsSummary; - this.workExperiences = resourceView.experiences; - this.experiencesSummary = resourceView.experiencesSummary; - this.profileSummary = resourceView.profileSummary; - this.skills = resourceView.skills.map((skill: Skill) => skill.skill.name); - this.skillsSummary = resourceView.skillsSummary; - this.viewName = resourceView.type; - - if (resourceView.revisionType === RevisionType.PENDING) { - this.isRevision = true; - } else { - this.isRevision = false; - } - if (profileViews) { - this.revisionViewLoaded.emit({ - isRevision: this.isRevision, - currentViewName: this.viewName, - resourceViews: profileViews, - }); - } else { - this.revisionViewLoaded.emit({ - isRevision: this.isRevision, - currentViewName: this.viewName, - }); - } - }); + this.sharedStore.dispatch(getViewById({ viewId: this.viewID })); + this.sharedStore + .select(selectView) + .pipe(takeUntil(this.$destroyed)) + .subscribe(resourceView => { + if (resourceView) { + this.certifications = resourceView.certifications; + this.educations = resourceView.educations; + this.educationsSummary = resourceView.educationsSummary; + this.workExperiences = resourceView.experiences; + this.experiencesSummary = resourceView.experiencesSummary; + this.profileSummary = resourceView.profileSummary; + this.skills = resourceView.skills.map((skill: Skill) => skill.skill.name); + this.skillsSummary = resourceView.skillsSummary; + this.viewName = resourceView.type; + + if (resourceView.revisionType === RevisionType.PENDING) { + this.isRevision = true; + } else { + this.isRevision = false; + } + if (profileViews) { + this.revisionViewLoaded.emit({ + isRevision: this.isRevision, + currentViewName: this.viewName, + resourceViews: profileViews, + }); + } else { + this.revisionViewLoaded.emit({ + isRevision: this.isRevision, + currentViewName: this.viewName, + }); + } + } + }); + // this.resourceService.getViewById(viewID).subscribe(resourceView => { + // this.certifications = resourceView.certifications; + // this.educations = resourceView.educations; + // this.educationsSummary = resourceView.educationsSummary; + // this.workExperiences = resourceView.experiences; + // this.experiencesSummary = resourceView.experiencesSummary; + // this.profileSummary = resourceView.profileSummary; + // this.skills = resourceView.skills.map((skill: Skill) => skill.skill.name); + // this.skillsSummary = resourceView.skillsSummary; + // this.viewName = resourceView.type; + + // if (resourceView.revisionType === RevisionType.PENDING) { + // this.isRevision = true; + // } else { + // this.isRevision = false; + // } + // if (profileViews) { + // this.revisionViewLoaded.emit({ + // isRevision: this.isRevision, + // currentViewName: this.viewName, + // resourceViews: profileViews, + // }); + // } else { + // this.revisionViewLoaded.emit({ + // isRevision: this.isRevision, + // currentViewName: this.viewName, + // }); + // } + // }); } openRejectionDialog() { @@ -237,14 +335,22 @@ export class ResourceProfileContentComponent implements OnInit, OnChanges { this.modalService.confirmEventSubject.subscribe(() => { this.modalService.close(); - this.resourceService - .approveOrDenyRevision( - this.viewID, + this.sharedStore.dispatch( + approveOrDenyRevision({ + viewId: this.viewID, // eslint-disable-next-line @typescript-eslint/dot-notation - this.viewResourceProfileForm.controls['rejectionComments'].value, - false, - ) - .subscribe(); + comment: this.viewResourceProfileForm.controls['rejectionComments'].value, + approval: false, + }), + ); + // this.resourceService + // .approveOrDenyRevision( + // this.viewID, + // // eslint-disable-next-line @typescript-eslint/dot-notation + // this.viewResourceProfileForm.controls['rejectionComments'].value, + // false, + // ) + // .subscribe(); this.modalService.confirmEventSubject.unsubscribe(); this.router.navigate(['../../manage-resources'], { relativeTo: this.route }).then(() => { window.location.reload(); @@ -273,7 +379,15 @@ export class ResourceProfileContentComponent implements OnInit, OnChanges { this.modalService.confirmEventSubject.subscribe(() => { this.modalService.close(); - this.resourceService.approveOrDenyRevision(this.viewID, '', true).subscribe(); + this.sharedStore.dispatch( + approveOrDenyRevision({ + viewId: this.viewID, + // eslint-disable-next-line @typescript-eslint/dot-notation + comment: '', + approval: true, + }), + ); + // this.resourceService.approveOrDenyRevision(this.viewID, '', true).subscribe(); this.modalService.confirmEventSubject.unsubscribe(); this.router.navigate(['../../manage-resources'], { relativeTo: this.route }).then(() => { window.location.reload(); diff --git a/libs/client/onboarding-client/business-owner/features/feature-view-resource-profile/src/lib/resource-profile/resource-profile.component.ts b/libs/client/onboarding-client/business-owner/features/feature-view-resource-profile/src/lib/resource-profile/resource-profile.component.ts index 6da6b6d4..4fbf1978 100644 --- a/libs/client/onboarding-client/business-owner/features/feature-view-resource-profile/src/lib/resource-profile/resource-profile.component.ts +++ b/libs/client/onboarding-client/business-owner/features/feature-view-resource-profile/src/lib/resource-profile/resource-profile.component.ts @@ -6,9 +6,13 @@ import { OnboardingClientState, getResourceInformationById, selectResourceDetails, + selectView, + getViewById, + selectRevision, + editResourceView, } from '@tempus/client/onboarding-client/shared/data-access'; import { LoadView, ProjectResource } from '@tempus/shared-domain'; -import { skip, Subject, take, takeUntil } from 'rxjs'; +import { skip, Subject, takeUntil } from 'rxjs'; import { BusinessOwnerState, getOriginalResume, @@ -86,12 +90,23 @@ export class ResourceProfileComponent implements OnInit, OnDestroy { this.changeDetector.detectChanges(); const viewId = parseInt(this.route.snapshot.queryParamMap.get('viewId') || '0', 10); if (viewId) { - this.resourceService - .getViewById(viewId) - .pipe(take(1)) + this.sharedStore + .select(selectView) + .pipe(takeUntil(this.$destroyed)) .subscribe(view => { - this.newViewForm.setFormDataFromView(view); + if (view) { + this.newViewForm.setFormDataFromView(view); + } else { + this.sharedStore.dispatch(getViewById({ viewId })); + } }); + + // this.resourceService + // .getViewById(viewId) + // .pipe(take(1)) + // .subscribe(view => { + // this.newViewForm.setFormDataFromView(view); + // }); } } @@ -103,12 +118,23 @@ export class ResourceProfileComponent implements OnInit, OnDestroy { submitChanges() { const viewId = parseInt(this.route.snapshot.queryParamMap.get('viewId') || '0', 10); const newView = this.newViewForm.generateNewView(); - this.resourceService - .editResourceView(viewId, newView) - .pipe(take(1)) - .subscribe(view => { - this.router.navigate([], { queryParams: { viewId: view.id } }).then(() => window.location.reload()); + this.sharedStore.dispatch(editResourceView({ viewId, newView })); + this.sharedStore + .select(selectRevision) + .pipe(skip(1), takeUntil(this.$destroyed)) + .subscribe(revision => { + if (revision) { + this.router + .navigate([], { queryParams: { viewId: revision.id } }) + .then(() => window.location.reload()); + } }); + // this.resourceService + // .editResourceView(viewId, newView) + // .pipe(take(1)) + // .subscribe(view => { + // this.router.navigate([], { queryParams: { viewId: view.id } }).then(() => window.location.reload()); + // }); this.closeEditView(); } diff --git a/libs/client/onboarding-client/business-owner/features/feature-view-resource-profile/src/lib/user-bar/user-bar.component.ts b/libs/client/onboarding-client/business-owner/features/feature-view-resource-profile/src/lib/user-bar/user-bar.component.ts index 57459a8f..d6e60695 100644 --- a/libs/client/onboarding-client/business-owner/features/feature-view-resource-profile/src/lib/user-bar/user-bar.component.ts +++ b/libs/client/onboarding-client/business-owner/features/feature-view-resource-profile/src/lib/user-bar/user-bar.component.ts @@ -1,9 +1,16 @@ import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core'; import { LoadView, ProjectResource, ViewNames } from '@tempus/shared-domain'; -import { OnboardingClientResourceService } from '@tempus/client/onboarding-client/shared/data-access'; +import { + downloadProfileByViewId, + OnboardingClientResourceService, + OnboardingClientState, + selectDownloadProfile, +} from '@tempus/client/onboarding-client/shared/data-access'; import { ActivatedRoute, Router } from '@angular/router'; import { FormBuilder } from '@angular/forms'; import { ButtonType } from '@tempus/client/shared/ui-components/presentational'; +import { Store } from '@ngrx/store'; +import { Subject, takeUntil } from 'rxjs'; @Component({ selector: 'tempus-user-bar', @@ -36,6 +43,8 @@ export class UserBarComponent implements OnChanges { viewResourceProfilePrefx = 'viewResourceProfile.'; + destroyed$ = new Subject(); + @Output() newViewSelected = new EventEmitter(); @Output() editViewSelected = new EventEmitter(); @@ -45,6 +54,7 @@ export class UserBarComponent implements OnChanges { private resourceService: OnboardingClientResourceService, private fb: FormBuilder, private router: Router, + private sharedStore: Store, ) {} ngOnChanges(): void { @@ -62,14 +72,27 @@ export class UserBarComponent implements OnChanges { downloadProfile() { // Taken from https://stackoverflow.com/questions/52154874/angular-6-downloading-file-from-rest-api - this.resourceService.downloadProfile(this.currentViewID).subscribe(data => { - const downloadURL = window.URL.createObjectURL(data); - const link = document.createElement('a'); - link.href = downloadURL; - const index = this.viewIDs.indexOf(this.currentViewID); - link.download = `${this.resourceName}-${this.viewNames[index]}`; - link.click(); - }); + this.sharedStore.dispatch(downloadProfileByViewId({ viewId: this.currentViewID })); + + this.sharedStore + .select(selectDownloadProfile) + .pipe(takeUntil(this.destroyed$)) + .subscribe(data => { + const downloadURL = window.URL.createObjectURL(data); + const link = document.createElement('a'); + link.href = downloadURL; + const index = this.viewIDs.indexOf(this.currentViewID); + link.download = `${this.resourceName}-${this.viewNames[index]}`; + link.click(); + }); + // this.resourceService.downloadProfile(this.currentViewID).subscribe(data => { + // const downloadURL = window.URL.createObjectURL(data); + // const link = document.createElement('a'); + // link.href = downloadURL; + // const index = this.viewIDs.indexOf(this.currentViewID); + // link.download = `${this.resourceName}-${this.viewNames[index]}`; + // link.click(); + // }); } onClick(optionSelected: string): void { diff --git a/libs/client/onboarding-client/resource/data-access/src/lib/+state/resource/resource.reducers.ts b/libs/client/onboarding-client/resource/data-access/src/lib/+state/resource/resource.reducers.ts index bf37ed61..74d8eb10 100644 --- a/libs/client/onboarding-client/resource/data-access/src/lib/+state/resource/resource.reducers.ts +++ b/libs/client/onboarding-client/resource/data-access/src/lib/+state/resource/resource.reducers.ts @@ -10,7 +10,7 @@ export interface ResourceState { lastName: string | null; email: string | null; views: View[] | null; - // resume: Blob | null; + resume: Blob | null; totalViewsData: number; error: Error | null; } @@ -20,7 +20,7 @@ export const initialState: ResourceState = { lastName: null, email: null, views: null, - // resume: null, + resume: null, totalViewsData: 0, error: null, }; diff --git a/libs/client/onboarding-client/resource/features/feature-profile/src/lib/edit-profile/edit-profile.component.ts b/libs/client/onboarding-client/resource/features/feature-profile/src/lib/edit-profile/edit-profile.component.ts index 41721ffc..e19d6dcc 100644 --- a/libs/client/onboarding-client/resource/features/feature-profile/src/lib/edit-profile/edit-profile.component.ts +++ b/libs/client/onboarding-client/resource/features/feature-profile/src/lib/edit-profile/edit-profile.component.ts @@ -1,12 +1,24 @@ import { Component, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'; -import { OnboardingClientResourceService } from '@tempus/client/onboarding-client/shared/data-access'; -import { Subject, take, takeUntil } from 'rxjs'; +import { + editResourceView, + getAllViewsByResourceId, + getResourceInformation, + getViewById, + OnboardingClientResourceService, + OnboardingClientState, + selectResourceDetails, + selectResourceViews, + selectRevision, + selectView, +} from '@tempus/client/onboarding-client/shared/data-access'; +import { skip, Subject, take, takeUntil } from 'rxjs'; import { TranslateService } from '@ngx-translate/core'; import { ModalService, CustomModalType, ModalType } from '@tempus/client/shared/ui-components/modal'; import { EditViewFormComponent } from '@tempus/onboarding-client/shared/feature-edit-view-form'; import { ActivatedRoute, Router } from '@angular/router'; import { ViewType } from '@tempus/shared-domain'; import { sortViewsByLatestUpdated } from '@tempus/client/shared/util'; +import { Store } from '@ngrx/store'; @Component({ selector: 'tempus-edit-profile', @@ -20,6 +32,7 @@ export class EditProfileComponent implements OnInit, OnDestroy { private route: ActivatedRoute, private resourceService: OnboardingClientResourceService, private translateService: TranslateService, + private sharedStore: Store, ) { const { currentLang } = translateService; // eslint-disable-next-line no-param-reassign @@ -35,31 +48,64 @@ export class EditProfileComponent implements OnInit, OnDestroy { destroyed$ = new Subject(); ngOnInit(): void { - this.resourceService - .getResourceInformation() + this.sharedStore.dispatch(getResourceInformation()); + this.sharedStore + .select(selectResourceDetails) .pipe(take(1)) .subscribe(resData => { - const resourceId = resData.id; + const resourceId = resData.userId; // Load Secondary view from route const viewId = parseInt(this.route.snapshot.paramMap.get('id') || '0', 10); if (viewId) { - this.resourceService - .getViewById(viewId) - .pipe(take(1)) + this.sharedStore + .select(selectView) + .pipe(takeUntil(this.destroyed$)) .subscribe(view => { - this.newViewForm.setFormDataFromView(view); + if (view) { + this.newViewForm.setFormDataFromView(view); + } else { + this.sharedStore.dispatch(getViewById({ viewId })); + } }); + // this.resourceService + // .getViewById(viewId) + // .pipe(take(1)) + // .subscribe(view => { + // this.newViewForm.setFormDataFromView(view); + // }); } else { // Display latest Primary view - this.resourceService - .getResourceProfileViews(resourceId) + + this.sharedStore + .select(selectResourceViews) .pipe(takeUntil(this.destroyed$)) .subscribe(data => { - let filteredAndSortedViews = data.views.filter(view => view.viewType === ViewType.PRIMARY); - filteredAndSortedViews = sortViewsByLatestUpdated(filteredAndSortedViews); - this.newViewForm.setFormDataFromView(filteredAndSortedViews[0]); + if (data) { + let filteredAndSortedViews = data.views?.filter( + (view: { viewType: ViewType }) => view.viewType === ViewType.PRIMARY, + ); + filteredAndSortedViews = sortViewsByLatestUpdated(filteredAndSortedViews); + this.newViewForm.setFormDataFromView(filteredAndSortedViews[0]); + } else { + this.sharedStore.dispatch( + getAllViewsByResourceId({ + resourceId, + pageSize: 1000, + pageNum: 0, + }), + ); + } }); + + // this.resourceService + // .getResourceProfileViews(resourceId) + // .pipe(takeUntil(this.destroyed$)) + // .subscribe(data => { + // let filteredAndSortedViews = data.views.filter(view => view.viewType === ViewType.PRIMARY); + // filteredAndSortedViews = sortViewsByLatestUpdated(filteredAndSortedViews); + // this.newViewForm.setFormDataFromView(filteredAndSortedViews[0]); + // }); } }); } @@ -72,23 +118,43 @@ export class EditProfileComponent implements OnInit, OnDestroy { updateView() { const newView = this.newViewForm.generateNewView(); - this.resourceService - .editResourceView(this.newViewForm.currentViewId, newView) - .pipe(take(1)) + this.sharedStore.dispatch(editResourceView({ viewId: this.newViewForm.currentViewId, newView })); + this.sharedStore + .select(selectRevision) + .pipe(skip(1), takeUntil(this.destroyed$)) .subscribe(revision => { - if (newView.viewType === ViewType.PRIMARY) { - window.location.reload(); - } else { - // Navigate to new view - this.router - .navigate(['../', revision.views[revision.views.length - 1]?.id], { - relativeTo: this.route, - }) - .then(() => { - window.location.reload(); - }); + if (revision) { + if (newView.viewType === ViewType.PRIMARY) { + window.location.reload(); + } else { + // Navigate to new view + this.router + .navigate(['../', revision.views[revision.views.length - 1]?.id], { + relativeTo: this.route, + }) + .then(() => { + window.location.reload(); + }); + } } }); + // this.resourceService + // .editResourceView(this.newViewForm.currentViewId, newView) + // .pipe(take(1)) + // .subscribe(revision => { + // if (newView.viewType === ViewType.PRIMARY) { + // window.location.reload(); + // } else { + // // Navigate to new view + // this.router + // .navigate(['../', revision.views[revision.views.length - 1]?.id], { + // relativeTo: this.route, + // }) + // .then(() => { + // window.location.reload(); + // }); + // } + // }); } openSubmitConfirmation() { diff --git a/libs/client/onboarding-client/resource/features/feature-profile/src/lib/profile/profile.component.ts b/libs/client/onboarding-client/resource/features/feature-profile/src/lib/profile/profile.component.ts index ebeae6f4..e1c93dd4 100644 --- a/libs/client/onboarding-client/resource/features/feature-profile/src/lib/profile/profile.component.ts +++ b/libs/client/onboarding-client/resource/features/feature-profile/src/lib/profile/profile.component.ts @@ -1,11 +1,15 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { getResourceInformation, + getResourceInformationById, + getViewById, OnboardingClientResourceService, OnboardingClientState, selectResourceDetails, + selectResourceViews, + selectView, } from '@tempus/client/onboarding-client/shared/data-access'; -import { Subject, take, takeUntil } from 'rxjs'; +import { skip, Subject, take, takeUntil } from 'rxjs'; import { ButtonType } from '@tempus/client/shared/ui-components/presentational'; import { ICreateExperienceDto, @@ -21,7 +25,6 @@ import { getResourceOriginalResumeById, selectDownloadProfile, selectResourceOriginalResume, - TempusResourceState, } from '@tempus/client/onboarding-client/resource/data-access'; import { TranslateService } from '@ngx-translate/core'; import { sortViewsByLatestUpdated } from '@tempus/client/shared/util'; @@ -43,10 +46,8 @@ export class ProfileComponent implements OnInit, OnDestroy { totalViews = 0; constructor( - private resourceService: OnboardingClientResourceService, private translateService: TranslateService, private sharedStore: Store, - private resourceStore: Store, private router: Router, private route: ActivatedRoute, ) { @@ -105,34 +106,26 @@ export class ProfileComponent implements OnInit, OnDestroy { editViewEnabled = false; ngOnInit(): void { + this.sharedStore.dispatch(getResourceInformation()); this.sharedStore .select(selectResourceDetails) .pipe(take(1)) .subscribe(data => { - if (data) { - this.userId = data.userId; - this.firstName = data.firstName || ''; - this.lastName = data.lastName || ''; - } else { - this.sharedStore.dispatch(getResourceInformation()); - } + this.userId = data.userId; + this.firstName = data.firstName || ''; + this.lastName = data.lastName || ''; }); - this.resourceStore + this.sharedStore.dispatch(getResourceOriginalResumeById({ resourceId: this.userId })); + this.sharedStore .select(selectResourceOriginalResume) .pipe(takeUntil(this.destroyed$)) .subscribe(blob => { if (blob) { this.resume = new File([blob], 'original-resume.pdf'); - } else { - this.resourceStore.dispatch(getResourceOriginalResumeById({ resourceId: this.userId })); } }); - this.resourceStore.dispatch( - getAllViewsByResourceId({ resourceId: this.userId, pageSize: this.pageSize, pageNum: this.pageNum }), - ); - // display latest primary view // this.resourceStore // .select(selectResourceViews) @@ -161,11 +154,17 @@ export class ProfileComponent implements OnInit, OnDestroy { const viewId = parseInt(this.route.snapshot.paramMap.get('id') || '0', 10); if (viewId) { - this.resourceService.getViewById(viewId).subscribe(view => { - this.pageTitle = view.type; - this.loadView(view); - this.dataLoaded = true; - }); + this.sharedStore.dispatch(getViewById({ viewId })); + this.sharedStore + .select(selectView) + .pipe(takeUntil(this.destroyed$)) + .subscribe(resourceView => { + if (resourceView) { + this.pageTitle = resourceView?.type || ''; + this.loadView(resourceView); + this.dataLoaded = true; + } + }); } else { // Display Primary view this.translateService @@ -175,43 +174,64 @@ export class ProfileComponent implements OnInit, OnDestroy { this.pageTitle = data; }); - this.resourceService.getResourceProfileViews(this.userId).subscribe(data => { - let filteredAndSortedViews = data.views?.filter(view => view.viewType === ViewType.PRIMARY); - filteredAndSortedViews = sortViewsByLatestUpdated(filteredAndSortedViews); - this.loadView(filteredAndSortedViews[0]); - this.dataLoaded = true; - }); + this.sharedStore.dispatch( + getAllViewsByResourceId({ resourceId: this.userId, pageSize: this.pageSize, pageNum: this.pageNum }), + ); + this.sharedStore + .select(selectResourceViews) + .pipe(takeUntil(this.destroyed$)) + .subscribe(data => { + let filteredAndSortedViews = data.views?.filter(view => view.viewType === ViewType.PRIMARY); + filteredAndSortedViews = sortViewsByLatestUpdated(filteredAndSortedViews); + this.loadView(filteredAndSortedViews[0]); + this.dataLoaded = true; + }); + + // this.resourceService.getResourceProfileViews(this.userId).subscribe(data => { + // let filteredAndSortedViews = data.views?.filter(view => view.viewType === ViewType.PRIMARY); + // filteredAndSortedViews = sortViewsByLatestUpdated(filteredAndSortedViews); + // this.loadView(filteredAndSortedViews[0]); + // this.dataLoaded = true; + // }); } - this.resourceService.getResourceInformation().subscribe(resData => { - this.userId = resData.id; - this.fullName = `${resData.firstName} ${resData.lastName}`; - - // Use viewId from route - // const viewId = parseInt(this.route.snapshot.paramMap.get('id') || '0', 10); - // if (viewId) { - // this.resourceService.getViewById(viewId).subscribe(view => { - // this.pageTitle = view.type; - // this.loadView(view); - // this.dataLoaded = true; - // }); - // } else { - // // Display Primary view - // this.translateService - // .get('onboardingResourceProfile.myProfile') - // .pipe(take(1)) - // .subscribe(data => { - // this.pageTitle = data; - // }); - - // this.resourceService.getResourceProfileViews(this.userId).subscribe(data => { - // let filteredAndSortedViews = data.views?.filter(view => view.viewType === ViewType.PRIMARY); - // filteredAndSortedViews = sortViewsByLatestUpdated(filteredAndSortedViews); - // this.loadView(filteredAndSortedViews[0]); - // this.dataLoaded = true; - // }); - // } - }); + this.sharedStore.dispatch(getResourceInformationById({ resourceId: this.userId })); + this.sharedStore + .select(selectResourceDetails) + .pipe(skip(1), takeUntil(this.destroyed$)) + .subscribe(data => { + this.userId = data.userId; + this.fullName = `${data.firstName} ${data.lastName}`; + }); + // this.resourceService.getResourceInformation().subscribe(resData => { + // this.userId = resData.id; + // this.fullName = `${resData.firstName} ${resData.lastName}`; + + // Use viewId from route + // const viewId = parseInt(this.route.snapshot.paramMap.get('id') || '0', 10); + // if (viewId) { + // this.resourceService.getViewById(viewId).subscribe(view => { + // this.pageTitle = view.type; + // this.loadView(view); + // this.dataLoaded = true; + // }); + // } else { + // // Display Primary view + // this.translateService + // .get('onboardingResourceProfile.myProfile') + // .pipe(take(1)) + // .subscribe(data => { + // this.pageTitle = data; + // }); + + // this.resourceService.getResourceProfileViews(this.userId).subscribe(data => { + // let filteredAndSortedViews = data.views?.filter(view => view.viewType === ViewType.PRIMARY); + // filteredAndSortedViews = sortViewsByLatestUpdated(filteredAndSortedViews); + // this.loadView(filteredAndSortedViews[0]); + // this.dataLoaded = true; + // }); + // } + // }); } loadView(view: View) { @@ -240,8 +260,8 @@ export class ProfileComponent implements OnInit, OnDestroy { downloadProfile() { // Taken from https://stackoverflow.com/questions/52154874/angular-6-downloading-file-from-rest-api - this.resourceStore.dispatch(downloadProfileByViewId({ viewId: this.currentViewId })); - this.resourceStore + this.sharedStore.dispatch(downloadProfileByViewId({ viewId: this.currentViewId })); + this.sharedStore .select(selectDownloadProfile) .pipe(takeUntil(this.destroyed$)) .subscribe(data => { diff --git a/libs/client/onboarding-client/resource/features/feature-views/src/lib/create-new-view/create-new-view.component.ts b/libs/client/onboarding-client/resource/features/feature-views/src/lib/create-new-view/create-new-view.component.ts index 8a7e64a3..0add5acc 100644 --- a/libs/client/onboarding-client/resource/features/feature-views/src/lib/create-new-view/create-new-view.component.ts +++ b/libs/client/onboarding-client/resource/features/feature-views/src/lib/create-new-view/create-new-view.component.ts @@ -1,7 +1,17 @@ import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; +import { Store } from '@ngrx/store'; import { TranslateService } from '@ngx-translate/core'; -import { OnboardingClientResourceService } from '@tempus/client/onboarding-client/shared/data-access'; +import { + createResourceView, + getAllViewsByResourceId, + getResourceInformation, + OnboardingClientResourceService, + OnboardingClientState, + selectResourceDetails, + selectResourceViews, + selectView, +} from '@tempus/client/onboarding-client/shared/data-access'; import { CustomModalType, ModalService, ModalType } from '@tempus/client/shared/ui-components/modal'; import { EditViewFormComponent } from '@tempus/onboarding-client/shared/feature-edit-view-form'; import { RevisionType, ViewType } from '@tempus/shared-domain'; @@ -20,6 +30,7 @@ export class CreateNewViewComponent implements OnInit, OnDestroy { public modalService: ModalService, private resourceService: OnboardingClientResourceService, private translateService: TranslateService, + private sharedStore: Store, ) { const { currentLang } = translateService; // eslint-disable-next-line no-param-reassign @@ -34,13 +45,21 @@ export class CreateNewViewComponent implements OnInit, OnDestroy { destroyed$ = new Subject(); ngOnInit(): void { - this.resourceService - .getResourceInformation() + this.sharedStore.dispatch(getResourceInformation()); + this.sharedStore + .select(selectResourceDetails) .pipe(take(1)) .subscribe(resData => { - this.userId = resData.id; - this.resourceService - .getResourceProfileViews(this.userId) + this.userId = resData.userId; + this.sharedStore.dispatch( + getAllViewsByResourceId({ + resourceId: this.userId, + pageSize: 1000, + pageNum: 0, + }), + ); + this.sharedStore + .select(selectResourceViews) .pipe(takeUntil(this.destroyed$)) .subscribe(data => { const approvedPrimaryView = data.views?.find( @@ -52,6 +71,25 @@ export class CreateNewViewComponent implements OnInit, OnDestroy { } }); }); + + // this.resourceService + // .getResourceInformation() + // .pipe(take(1)) + // .subscribe(resData => { + // this.userId = resData.id; + // this.resourceService + // .getResourceProfileViews(this.userId) + // .pipe(takeUntil(this.destroyed$)) + // .subscribe(data => { + // const approvedPrimaryView = data.views?.find( + // view => view.revisionType === RevisionType.APPROVED && view.viewType === ViewType.PRIMARY, + // ); + // if (approvedPrimaryView) { + // this.newViewForm.setFormDataFromView(approvedPrimaryView); + // this.newViewForm.enableViewNameField(); + // } + // }); + // }); } submitChanges() { @@ -62,14 +100,24 @@ export class CreateNewViewComponent implements OnInit, OnDestroy { createNewView() { const newView = this.newViewForm.generateNewView(); - this.resourceService - .createSecondaryView(this.userId, newView) - .pipe(take(1)) - .subscribe(view => { - this.router.navigate(['../', view.id], { + this.sharedStore.dispatch(createResourceView({ resourceId: this.userId, newView })); + this.sharedStore + .select(selectView) + .pipe(takeUntil(this.destroyed$)) + .subscribe(resourceView => { + this.router.navigate(['../', resourceView?.id], { relativeTo: this.route, }); }); + + // this.resourceService + // .createSecondaryView(this.userId, newView) + // .pipe(take(1)) + // .subscribe(view => { + // this.router.navigate(['../', view.id], { + // relativeTo: this.route, + // }); + // }); } openSubmitConfirmation() { diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.actions.ts b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.actions.ts index 802e9be0..2c0ed9b1 100644 --- a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.actions.ts +++ b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.actions.ts @@ -1,5 +1,6 @@ import { createAction, props } from '@ngrx/store'; -import { IUpdateResourceDto, ProjectResource, View } from '@tempus/shared-domain'; +import { ApproveViewDto } from '@tempus/api/shared/dto'; +import { ICreateViewDto, IUpdateResourceDto, ProjectResource, Revision, View } from '@tempus/shared-domain'; export const getResourceInformation = createAction('[Onboarding Client User Api] Get Resource Information'); @@ -74,6 +75,22 @@ export const updateInfoFailure = createAction( props<{ error: Error }>(), ); +// get view by id +export const getViewById = createAction( + '[Onboarding Client Profile Views API] Get View By Id', + props<{ viewId: number }>(), +); + +export const getViewByIdSuccess = createAction( + '[Onboarding Client Profile Views API] Get View By Id Success', + props<{ view: View }>(), +); + +export const getViewByIdFailure = createAction( + '[Onboarding Client Profile Views API] Get View By Id Failure', + props<{ error: Error }>(), +); + // get all views by resource id export const getAllViewsByResourceId = createAction( '[Onboarding Client Profile Views API] Get All Views By Resource Id', @@ -90,6 +107,38 @@ export const getAllViewsByResourceIdFailure = createAction( props<{ error: Error }>(), ); +// edit resource view +export const editResourceView = createAction( + '[Onboarding Client Profile Views API] Edit Resource View', + props<{ viewId: number; newView: ICreateViewDto }>(), +); + +export const editResourceViewSuccess = createAction( + '[Onboarding Client Profile Views API] Edit Resource View Success', + props<{ revision: Revision }>(), +); + +export const editResourceViewFailure = createAction( + '[Onboarding Client Profile Views API] Edit Resource View Failure', + props<{ error: Error }>(), +); + +// create resource view +export const createResourceView = createAction( + '[Onboarding Client Profile Views API] Create Resource View', + props<{ resourceId: number; newView: ICreateViewDto }>(), +); + +export const createResourceViewSuccess = createAction( + '[Onboarding Client Profile Views API] Create Resource View Success', + props<{ view: View }>(), +); + +export const createResourceViewFailure = createAction( + '[Onboarding Client Profile Views API] Create Resource View Failure', + props<{ error: Error }>(), +); + // get original resume by resource id export const getResourceOriginalResumeById = createAction( '[Onboarding Client User API] Get Original Resume By Resource Id', @@ -121,3 +170,19 @@ export const downloadProfileByViewIdFailure = createAction( '[Onboarding Client Profile Views API] Download Profile By View Id Failure', props<{ error: Error }>(), ); + +// approve or deny revision +export const approveOrDenyRevision = createAction( + '[Onboarding Client Profile Views API] Approve Or Deny Revision', + props<{ viewId: number; comment: string; approval: boolean }>(), +); + +export const approveOrDenyRevisionSuccess = createAction( + '[Onboarding Client Profile Views API] Approve Or Deny Revision Success', + props<{ approveOrDeny: ApproveViewDto }>(), +); + +export const approveOrDenyRevisionFailure = createAction( + '[Onboarding Client Profile Views API] Approve Or Deny Revision Failure', + props<{ error: Error }>(), +); diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.effects.ts b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.effects.ts index 17c3a1f4..9b89ae77 100644 --- a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.effects.ts +++ b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.effects.ts @@ -9,6 +9,11 @@ import { OnboardingClientViewsService, } from '../../services'; import { + approveOrDenyRevision, + approveOrDenyRevisionSuccess, + createResourceView, + createResourceViewFailure, + createResourceViewSuccess, getAllViewsByResourceId, getResourceInformation, getResourceInformationById, @@ -20,14 +25,21 @@ import { updateUserInfoSuccess, } from './resource.actions'; import { + approveOrDenyRevisionFailure, downloadProfileByViewId, downloadProfileByViewIdFailure, downloadProfileByViewIdSuccess, + editResourceView, + editResourceViewFailure, + editResourceViewSuccess, getAllViewsByResourceIdFailure, getAllViewsByResourceIdSuccess, getResourceInformationByIdFailure, getResourceInformationSuccess, getResourceOriginalResumeByIdSuccess, + getViewById, + getViewByIdFailure, + getViewByIdSuccess, } from '.'; import { OnboardingClientState } from '../onboardingClient.state'; @@ -112,6 +124,20 @@ export class ResourceEffects { ), ); + getViewById$ = createEffect(() => + this.actions$.pipe( + ofType(getViewById), + switchMap(data => + this.viewsService.getViewById(data.viewId).pipe( + map(res => { + return getViewByIdSuccess({ view: res }); + }), + catchError(error => of(getViewByIdFailure({ error }))), + ), + ), + ), + ); + getAllViews$ = createEffect(() => this.actions$.pipe( ofType(getAllViewsByResourceId), @@ -126,6 +152,34 @@ export class ResourceEffects { ), ); + editResourceView$ = createEffect(() => + this.actions$.pipe( + ofType(editResourceView), + switchMap(data => + this.viewsService.editResourceView(data.viewId, data.newView).pipe( + map(res => { + return editResourceViewSuccess({ revision: res }); + }), + catchError(error => of(editResourceViewFailure({ error }))), + ), + ), + ), + ); + + createResourceView$ = createEffect(() => + this.actions$.pipe( + ofType(createResourceView), + switchMap(data => + this.viewsService.createSecondaryView(data.resourceId, data.newView).pipe( + map(res => { + return createResourceViewSuccess({ view: res }); + }), + catchError(error => of(createResourceViewFailure({ error }))), + ), + ), + ), + ); + getResourceOriginalResume$ = createEffect(() => this.actions$.pipe( ofType(getResourceOriginalResumeById), @@ -153,4 +207,18 @@ export class ResourceEffects { ), ), ); + + approveOrDeny$ = createEffect(() => + this.actions$.pipe( + ofType(approveOrDenyRevision), + switchMap(data => + this.viewsService.approveOrDenyRevision(data.viewId, data.comment, data.approval).pipe( + map(res => { + return approveOrDenyRevisionSuccess({ approveOrDeny: res }); + }), + catchError(error => of(approveOrDenyRevisionFailure({ error }))), + ), + ), + ), + ); } diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.reducers.ts b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.reducers.ts index 3cd30d88..ee440dc5 100644 --- a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.reducers.ts +++ b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.reducers.ts @@ -1,5 +1,6 @@ import { createReducer, on } from '@ngrx/store'; -import { ProjectResource, View } from '@tempus/shared-domain'; +import { ApproveViewDto } from '@tempus/api/shared/dto'; +import { ProjectResource, Revision, View } from '@tempus/shared-domain'; import { AsyncRequestState } from '../../enum'; import * as ResourceActions from './resource.actions'; @@ -11,7 +12,9 @@ export interface ResourceState { lastName: string | null; email: string | null; phoneNumber: string | null; - views: View[] | null; + views: View[] | []; + view: View | null; + revision: Revision | null; resume: Blob | null; totalViewsData: number; city: string | null; @@ -20,7 +23,8 @@ export interface ResourceState { linkedInLink: string | null; githubLink: string | null; otherLink: string | null; - projectResources: ProjectResource[] | null; + projectResources: ProjectResource[] | []; + approveOrDeny: ApproveViewDto | null; error: Error | null; } @@ -30,7 +34,9 @@ export const initialState: ResourceState = { lastName: null, email: null, phoneNumber: null, - views: null, + views: [], + view: null, + revision: null, resume: null, totalViewsData: 0, city: null, @@ -39,7 +45,8 @@ export const initialState: ResourceState = { linkedInLink: null, githubLink: null, otherLink: null, - projectResources: null, + projectResources: [], + approveOrDeny: null, error: null, }; @@ -139,6 +146,19 @@ export const resourceReducer = createReducer( status: AsyncRequestState.ERROR, error, })), + // getViewById + on(ResourceActions.getViewById, state => ({ ...state, status: AsyncRequestState.LOADING })), + on(ResourceActions.getViewByIdSuccess, (state, { view }) => ({ + ...state, + view, + status: AsyncRequestState.SUCCESS, + error: null, + })), + on(ResourceActions.getViewByIdFailure, (state, { error }) => ({ + ...state, + error, + status: AsyncRequestState.ERROR, + })), // getAllViewsByResourceId on(ResourceActions.getAllViewsByResourceId, state => ({ ...state, status: AsyncRequestState.LOADING })), on(ResourceActions.getAllViewsByResourceIdSuccess, (state, { views, totalViews }) => ({ @@ -153,6 +173,28 @@ export const resourceReducer = createReducer( error, status: AsyncRequestState.ERROR, })), + // editResourceView + on(ResourceActions.editResourceView, state => ({ ...state, status: AsyncRequestState.LOADING })), + on(ResourceActions.editResourceViewSuccess, (state, { revision }) => ({ + ...state, + revision, + status: AsyncRequestState.SUCCESS, + })), + on(ResourceActions.editResourceViewFailure, (state, { error }) => ({ + ...state, + error, + })), + // createResourceView + on(ResourceActions.createResourceView, state => ({ ...state, status: AsyncRequestState.LOADING })), + on(ResourceActions.createResourceViewSuccess, (state, { view }) => ({ + ...state, + view, + status: AsyncRequestState.SUCCESS, + })), + on(ResourceActions.createResourceViewFailure, (state, { error }) => ({ + ...state, + error, + })), // getResourceOriginalResumeById on(ResourceActions.getResourceOriginalResumeById, state => ({ ...state, status: AsyncRequestState.LOADING })), on(ResourceActions.getResourceOriginalResumeByIdSuccess, (state, { resume }) => ({ @@ -175,4 +217,15 @@ export const resourceReducer = createReducer( ...state, error, })), + // approveOrDenyView + on(ResourceActions.approveOrDenyRevision, state => ({ ...state, status: AsyncRequestState.LOADING })), + on(ResourceActions.approveOrDenyRevisionSuccess, (state, { approveOrDeny }) => ({ + ...state, + approveOrDeny, + status: AsyncRequestState.SUCCESS, + })), + on(ResourceActions.approveOrDenyRevisionFailure, (state, { error }) => ({ + ...state, + error, + })), ); diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.selectors.ts b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.selectors.ts index 4c8bd422..2fc88e06 100644 --- a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.selectors.ts +++ b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.selectors.ts @@ -29,10 +29,16 @@ export const selectResourceBasicDetails = createSelector(selectState, (state: Re return { firstName: state.firstName, lastName: state.lastName, email: state.email }; }); +export const selectView = createSelector(selectState, (state: ResourceState) => state.view); + export const selectResourceViews = createSelector(selectState, (state: ResourceState) => { return { views: state.views, totalViews: state.totalViewsData }; }); +export const selectRevision = createSelector(selectState, (state: ResourceState) => state.revision); + export const selectResourceOriginalResume = createSelector(selectState, (state: ResourceState) => state.resume); +export const selectApproveOrDeny = createSelector(selectState, (state: ResourceState) => state.approveOrDeny); + export const selectDownloadProfile = createSelector(selectState, (state: ResourceState) => state.resume); diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/services/onboarding-client-resource.service.ts b/libs/client/onboarding-client/shared/data-access/src/lib/services/onboarding-client-resource.service.ts index 1b374dc6..293fd1c8 100644 --- a/libs/client/onboarding-client/shared/data-access/src/lib/services/onboarding-client-resource.service.ts +++ b/libs/client/onboarding-client/shared/data-access/src/lib/services/onboarding-client-resource.service.ts @@ -70,6 +70,7 @@ export class OnboardingClientResourceService { } // TODO: look into moving to views service + // DELETE public getViewById(viewId: number): Observable { return this.http.get(`${this.url}/profile-view/view/${viewId}`).pipe(catchError(handleError)); } @@ -81,12 +82,14 @@ export class OnboardingClientResourceService { .pipe(catchError(handleError)); } + // DELETE public createSecondaryView(resourceId: number, newView: ICreateViewDto): Observable { return this.http .post(`${this.url}/profile-view/${resourceId}/new-view`, newView) .pipe(catchError(handleError)); } + // DELETE public editResourceView(viewId: number, newView: ICreateViewDto): Observable { return this.http.patch(`${this.url}/profile-view/${viewId}`, newView).pipe(catchError(handleError)); } @@ -104,6 +107,7 @@ export class OnboardingClientResourceService { return this.http.get(`${this.url}/profile-view/download-resume/${viewId}`, httpOptions); } + // DELETE public approveOrDenyRevision(id: number, comment: string, approval: boolean): Observable { return this.http .post(`${this.url}/profile-view/approve/${id}`, { comment, approval }) diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/services/onboarding-client-views.service.ts b/libs/client/onboarding-client/shared/data-access/src/lib/services/onboarding-client-views.service.ts index dc1b3d2a..76acaac9 100644 --- a/libs/client/onboarding-client/shared/data-access/src/lib/services/onboarding-client-views.service.ts +++ b/libs/client/onboarding-client/shared/data-access/src/lib/services/onboarding-client-views.service.ts @@ -1,7 +1,8 @@ import { HttpClient } from '@angular/common/http'; import { Inject, Injectable } from '@angular/core'; +import { ApproveViewDto } from '@tempus/api/shared/dto'; import { APP_CONFIG } from '@tempus/app-config'; -import { AppConfig, RevisionType, View } from '@tempus/shared-domain'; +import { AppConfig, ICreateViewDto, Revision, RevisionType, View } from '@tempus/shared-domain'; import { catchError, Observable } from 'rxjs'; import { handleError } from './errorHandler'; @@ -17,11 +18,14 @@ export class OnboardingClientViewsService { pageSize: number, ): Observable<{ views: View[]; totalPendingApprovals: number }> { return this.http - .get<{ views: View[]; totalPendingApprovals: number }>(`${this.url}/views?page=${page}&pageSize=${pageSize}`, { - params: { - status, + .get<{ views: View[]; totalPendingApprovals: number }>( + `${this.url}/views?page=${page}&pageSize=${pageSize}`, + { + params: { + status, + }, }, - }) + ) .pipe(catchError(handleError)); } @@ -31,7 +35,30 @@ export class OnboardingClientViewsService { pageSize: number, ): Observable<{ views: View[]; totalViews: number }> { return this.http - .get<{ views: View[]; totalViews: number }>(`${this.url}/${resourceId}?page=${page}&pageSize=${pageSize}`, {}) + .get<{ views: View[]; totalViews: number }>( + `${this.url}/${resourceId}?page=${page}&pageSize=${pageSize}`, + {}, + ) + .pipe(catchError(handleError)); + } + + public getViewById(viewId: number): Observable { + return this.http.get(`${this.url}/view/${viewId}`).pipe(catchError(handleError)); + } + + public editResourceView(viewId: number, newView: ICreateViewDto): Observable { + return this.http.patch(`${this.url}/${viewId}`, newView).pipe(catchError(handleError)); + } + + public createSecondaryView(resourceId: number, newView: ICreateViewDto): Observable { + return this.http + .post(`${this.url}/profile-view/${resourceId}/new-view`, newView) + .pipe(catchError(handleError)); + } + + public approveOrDenyRevision(id: number, comment: string, approval: boolean): Observable { + return this.http + .post(`${this.url}/profile-view/approve/${id}`, { comment, approval }) .pipe(catchError(handleError)); } } From ad9ce3fd2ed2292540d9ba45921d2dde8669b5d8 Mon Sep 17 00:00:00 2001 From: GCham5 Date: Sun, 11 Dec 2022 01:05:07 -0500 Subject: [PATCH 6/7] feat: add remaining stores --- .../create-new-view.component.ts | 60 ++++++++++++++----- .../src/lib/user-bar/user-bar.component.ts | 14 +++-- 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/libs/client/onboarding-client/business-owner/features/feature-view-resource-profile/src/lib/create-new-view/create-new-view.component.ts b/libs/client/onboarding-client/business-owner/features/feature-view-resource-profile/src/lib/create-new-view/create-new-view.component.ts index 124954e8..19dd52e6 100644 --- a/libs/client/onboarding-client/business-owner/features/feature-view-resource-profile/src/lib/create-new-view/create-new-view.component.ts +++ b/libs/client/onboarding-client/business-owner/features/feature-view-resource-profile/src/lib/create-new-view/create-new-view.component.ts @@ -1,10 +1,18 @@ import { Component, OnInit, ViewChild } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; +import { Store } from '@ngrx/store'; import { TranslateService } from '@ngx-translate/core'; -import { OnboardingClientResourceService } from '@tempus/client/onboarding-client/shared/data-access'; +import { + createResourceView, + getAllViewsByResourceId, + OnboardingClientResourceService, + OnboardingClientState, + selectResourceViews, + selectView, +} from '@tempus/client/onboarding-client/shared/data-access'; import { EditViewFormComponent } from '@tempus/onboarding-client/shared/feature-edit-view-form'; import { RevisionType, ViewType } from '@tempus/shared-domain'; -import { take } from 'rxjs'; +import { Subject, takeUntil } from 'rxjs'; @Component({ selector: 'tempus-business-owner-create-new-view', @@ -17,6 +25,7 @@ export class BusinessOwnerCreateNewViewComponent implements OnInit { private router: Router, private route: ActivatedRoute, private resourceService: OnboardingClientResourceService, + private sharedStore: Store, private translateService: TranslateService, ) { const { currentLang } = translateService; @@ -29,17 +38,30 @@ export class BusinessOwnerCreateNewViewComponent implements OnInit { resourceId = 0; + destroyed$ = new Subject(); + ngOnInit(): void { this.resourceId = parseInt(this.route.snapshot.paramMap.get('id') || '0', 10); // New view form loaded with latest approved Primary view - this.resourceService.getResourceProfileViews(this.resourceId).subscribe(data => { - const filteredViews = data.views.filter( - view => view.viewType === ViewType.PRIMARY && view.revisionType === RevisionType.APPROVED, - ); - this.newViewForm.setFormDataFromView(filteredViews[0]); - this.newViewForm.enableViewNameField(); - }); + this.sharedStore.dispatch(getAllViewsByResourceId({ resourceId: this.resourceId, pageNum: 0, pageSize: 1000 })); + this.sharedStore + .select(selectResourceViews) + .pipe(takeUntil(this.destroyed$)) + .subscribe(data => { + const filteredViews = data.views.filter( + view => view.viewType === ViewType.PRIMARY && view.revisionType === RevisionType.APPROVED, + ); + this.newViewForm.setFormDataFromView(filteredViews[0]); + this.newViewForm.enableViewNameField(); + }); + // this.resourceService.getResourceProfileViews(this.resourceId).subscribe(data => { + // const filteredViews = data.views.filter( + // view => view.viewType === ViewType.PRIMARY && view.revisionType === RevisionType.APPROVED, + // ); + // this.newViewForm.setFormDataFromView(filteredViews[0]); + // this.newViewForm.enableViewNameField(); + // }); } submitChanges() { @@ -50,15 +72,25 @@ export class BusinessOwnerCreateNewViewComponent implements OnInit { createNewView() { const newView = this.newViewForm.generateNewView(); - this.resourceService - .createSecondaryView(this.resourceId, newView) - .pipe(take(1)) - .subscribe(view => { + this.sharedStore.dispatch(createResourceView({ resourceId: this.resourceId, newView })); + this.sharedStore + .select(selectView) + .pipe(takeUntil(this.destroyed$)) + .subscribe(resourceView => { this.router.navigate(['../'], { - queryParams: { viewId: view.id }, + queryParams: { viewId: resourceView?.id }, relativeTo: this.route, }); }); + // this.resourceService + // .createSecondaryView(this.resourceId, newView) + // .pipe(take(1)) + // .subscribe(view => { + // this.router.navigate(['../'], { + // queryParams: { viewId: view.id }, + // relativeTo: this.route, + // }); + // }); } closeForm() { diff --git a/libs/client/onboarding-client/business-owner/features/feature-view-resource-profile/src/lib/user-bar/user-bar.component.ts b/libs/client/onboarding-client/business-owner/features/feature-view-resource-profile/src/lib/user-bar/user-bar.component.ts index d6e60695..572bb9b8 100644 --- a/libs/client/onboarding-client/business-owner/features/feature-view-resource-profile/src/lib/user-bar/user-bar.component.ts +++ b/libs/client/onboarding-client/business-owner/features/feature-view-resource-profile/src/lib/user-bar/user-bar.component.ts @@ -78,12 +78,14 @@ export class UserBarComponent implements OnChanges { .select(selectDownloadProfile) .pipe(takeUntil(this.destroyed$)) .subscribe(data => { - const downloadURL = window.URL.createObjectURL(data); - const link = document.createElement('a'); - link.href = downloadURL; - const index = this.viewIDs.indexOf(this.currentViewID); - link.download = `${this.resourceName}-${this.viewNames[index]}`; - link.click(); + if (data) { + const downloadURL = window.URL.createObjectURL(data); + const link = document.createElement('a'); + link.href = downloadURL; + const index = this.viewIDs.indexOf(this.currentViewID); + link.download = `${this.resourceName}-${this.viewNames[index]}`; + link.click(); + } }); // this.resourceService.downloadProfile(this.currentViewID).subscribe(data => { // const downloadURL = window.URL.createObjectURL(data); From 66519fe694a910d66f4083a3585cc802c9f38b95 Mon Sep 17 00:00:00 2001 From: GCham5 Date: Sun, 11 Dec 2022 02:59:39 -0500 Subject: [PATCH 7/7] chore: merge fixes --- .../shared/data-access/src/lib/+state/auth/auth.actions.ts | 3 ++- .../data-access/src/lib/+state/resource/resource.actions.ts | 2 ++ .../src/lib/+state/resource/resource.reducers.ts | 6 ++++++ .../src/lib/+state/resource/resource.selectors.ts | 1 + 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/+state/auth/auth.actions.ts b/libs/client/onboarding-client/shared/data-access/src/lib/+state/auth/auth.actions.ts index e5552e80..7e521e31 100644 --- a/libs/client/onboarding-client/shared/data-access/src/lib/+state/auth/auth.actions.ts +++ b/libs/client/onboarding-client/shared/data-access/src/lib/+state/auth/auth.actions.ts @@ -10,10 +10,11 @@ export const loginSuccess = createAction( props<{ accessToken: string; refreshToken: string; + // loggedInUserId: number; firstName: string; lastName: string; email: string; - roles: RoleType[]; + roles: RoleType[]; }>(), ); export const loginFailure = createAction('[Onboarding Client Auth Api] Login Failure', props<{ error: Error }>()); diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.actions.ts b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.actions.ts index 2c0ed9b1..4e4f45c3 100644 --- a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.actions.ts +++ b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.actions.ts @@ -11,6 +11,7 @@ export const getResourceInformationSuccess = createAction( firstName: string; lastName: string; email: string; + calEmail: string; phoneNumber: string; city: string; province: string; @@ -38,6 +39,7 @@ export const getResourceInformationByIdSuccess = createAction( firstName: string; lastName: string; email: string; + calEmail: string; phoneNumber: string; city: string; province: string; diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.reducers.ts b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.reducers.ts index ee440dc5..a4623f31 100644 --- a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.reducers.ts +++ b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.reducers.ts @@ -11,6 +11,7 @@ export interface ResourceState { firstName: string | null; lastName: string | null; email: string | null; + calEmail: string | null; phoneNumber: string | null; views: View[] | []; view: View | null; @@ -33,6 +34,7 @@ export const initialState: ResourceState = { firstName: null, lastName: null, email: null, + calEmail: null, phoneNumber: null, views: [], view: null, @@ -63,6 +65,7 @@ export const resourceReducer = createReducer( firstName, lastName, email, + calEmail, city, province, country, @@ -78,6 +81,7 @@ export const resourceReducer = createReducer( firstName, lastName, email, + calEmail, city, province, country, @@ -103,6 +107,7 @@ export const resourceReducer = createReducer( firstName, lastName, email, + calEmail, city, province, country, @@ -117,6 +122,7 @@ export const resourceReducer = createReducer( firstName, lastName, email, + calEmail, city, province, country, diff --git a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.selectors.ts b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.selectors.ts index 2fc88e06..138925ab 100644 --- a/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.selectors.ts +++ b/libs/client/onboarding-client/shared/data-access/src/lib/+state/resource/resource.selectors.ts @@ -14,6 +14,7 @@ export const selectResourceDetails = createSelector(selectState, (state: Resourc firstName: state.firstName, lastName: state.lastName, email: state.email, + calEmail: state.calEmail, phoneNumber: state.phoneNumber, city: state.city, province: state.province,