diff --git a/CHANGELOG.md b/CHANGELOG.md index ec6ea925..a8f7ba6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ please see [changelog_updates.md](docs/dev/changelog_updates.md). - Fixed Confirm and Delete buttons' behavior in confirmation modals ([#304](https://github.com/sovity/authority-portal/issues/304)) - Fixed final step not showing when registering a central component ([#305](https://github.com/sovity/authority-portal/issues/305)) - Fixed My Organization page not updated when switching between environments ([#255](https://github.com/sovity/authority-portal/issues/255)) +- Added live update when deactivating/reactivating users ([#287](https://github.com/sovity/authority-portal/issues/287)) - Fixed Website title not updating in some scenarios [#237](https://github.com/sovity/authority-portal/issues/237) - Fixed security vulnerabilities diff --git a/authority-portal-frontend/src/app/core/api/fake-backend/fake-backend.ts b/authority-portal-frontend/src/app/core/api/fake-backend/fake-backend.ts index 6083f606..66b8ae13 100644 --- a/authority-portal-frontend/src/app/core/api/fake-backend/fake-backend.ts +++ b/authority-portal-frontend/src/app/core/api/fake-backend/fake-backend.ts @@ -166,11 +166,13 @@ export const AUTHORITY_PORTAL_FAKE_BACKEND: FetchAPI = async ( .url('authority/users/*/deactivate') .on('PUT', (userId) => { + deactivateUser(userId); return ok(userId); }) .url('authority/users/*/reactivate') .on('PUT', (userId) => { + reactivateUser(userId); return ok(userId); }) diff --git a/authority-portal-frontend/src/app/core/api/fake-backend/impl/fake-organizations.ts b/authority-portal-frontend/src/app/core/api/fake-backend/impl/fake-organizations.ts index aa30dd25..c17fbfb3 100644 --- a/authority-portal-frontend/src/app/core/api/fake-backend/impl/fake-organizations.ts +++ b/authority-portal-frontend/src/app/core/api/fake-backend/impl/fake-organizations.ts @@ -551,6 +551,14 @@ export const getOrganizationDetails = ( ) as OrganizationDetailsDto; }; +export const findOrganizationByUserId = ( + userId: string, +): string | undefined => { + return TEST_ORGANIZATIONS.find((organization) => + organization.memberList.some((member) => member.userId === userId), + )?.id; +}; + export const getOrganizations = (): OrganizationDetailsDto[] => { return TEST_ORGANIZATIONS; }; diff --git a/authority-portal-frontend/src/app/core/api/fake-backend/impl/fake-users.ts b/authority-portal-frontend/src/app/core/api/fake-backend/impl/fake-users.ts index fdc4c35a..e19a4625 100644 --- a/authority-portal-frontend/src/app/core/api/fake-backend/impl/fake-users.ts +++ b/authority-portal-frontend/src/app/core/api/fake-backend/impl/fake-users.ts @@ -23,6 +23,7 @@ import { UserDeletionCheck, UserDetailDto, UserInfo, + UserRegistrationStatusDto, UserRoleDto, } from '@sovity.de/authority-portal-client'; import {Patcher, patchObj} from 'src/app/core/utils/object-utils'; @@ -32,6 +33,7 @@ import { } from '../../../utils/user-role-utils'; import { deleteOrganization, + findOrganizationByUserId, getOrganizationDetails, getParticipantAdmins, updateOrganization, @@ -447,12 +449,37 @@ export const clearApplicationRole = ( return {id: request.userId, changedDate: new Date()}; }; +const updateUserStatusOfOrganizationById = ( + userId: string, + status: UserRegistrationStatusDto, +) => { + const organizationId = findOrganizationByUserId(userId); + if (organizationId) { + updateOrganization(organizationId, (organization) => { + return { + ...organization, + memberList: organization.memberList.map((member) => { + if (member.userId === userId) { + return { + ...member, + registrationStatus: status, + }; + } + return member; + }), + }; + }); + } +}; + export const deactivateUser = (userId: string): IdResponse => { + updateUserStatusOfOrganizationById(userId, 'DEACTIVATED'); patchUser(userId, () => ({registrationStatus: 'DEACTIVATED'})); return {id: userId, changedDate: new Date()}; }; export const reactivateUser = (userId: string): IdResponse => { + updateUserStatusOfOrganizationById(userId, 'ACTIVE'); patchUser(userId, () => ({registrationStatus: 'ACTIVE'})); return {id: userId, changedDate: new Date()}; }; diff --git a/authority-portal-frontend/src/app/pages/authority-organization-detail-page/authority-organization-detail-page/authority-organization-detail-page.component.ts b/authority-portal-frontend/src/app/pages/authority-organization-detail-page/authority-organization-detail-page/authority-organization-detail-page.component.ts index f98488b9..8d6b3120 100644 --- a/authority-portal-frontend/src/app/pages/authority-organization-detail-page/authority-organization-detail-page/authority-organization-detail-page.component.ts +++ b/authority-portal-frontend/src/app/pages/authority-organization-detail-page/authority-organization-detail-page/authority-organization-detail-page.component.ts @@ -278,7 +278,7 @@ export class AuthorityOrganizationDetailPageComponent this.store.dispatch(RefreshOrganizations); } else { this.store - .dispatch(RefreshOrganization) + .dispatch(new RefreshOrganization()) .pipe(take(1)) .subscribe(() => { this.slideOverService.setSlideOverViews( diff --git a/authority-portal-frontend/src/app/pages/authority-organization-detail-page/state/authority-organization-detail-page-actions.ts b/authority-portal-frontend/src/app/pages/authority-organization-detail-page/state/authority-organization-detail-page-actions.ts index c41e96ef..79b9cd82 100644 --- a/authority-portal-frontend/src/app/pages/authority-organization-detail-page/state/authority-organization-detail-page-actions.ts +++ b/authority-portal-frontend/src/app/pages/authority-organization-detail-page/state/authority-organization-detail-page-actions.ts @@ -20,6 +20,7 @@ export class SetOrganizationId { export class RefreshOrganization { static readonly type = `[${tag}] Refresh Organization`; + constructor(public cb?: () => void) {} } export class ApproveOrganization { diff --git a/authority-portal-frontend/src/app/pages/authority-organization-detail-page/state/authority-organization-detail-page-state-impl.ts b/authority-portal-frontend/src/app/pages/authority-organization-detail-page/state/authority-organization-detail-page-state-impl.ts index 33890f04..6de7c506 100644 --- a/authority-portal-frontend/src/app/pages/authority-organization-detail-page/state/authority-organization-detail-page-state-impl.ts +++ b/authority-portal-frontend/src/app/pages/authority-organization-detail-page/state/authority-organization-detail-page-state-impl.ts @@ -29,6 +29,7 @@ import {SlideOverService} from 'src/app/core/services/slide-over.service'; import {Fetched} from 'src/app/core/utils/fetched'; import {ToastService} from 'src/app/shared/common/toast-notifications/toast.service'; import {RefreshOrganizations} from '../../authority-organization-list-page/authority-organization-list-page/state/authority-organization-list-page-actions'; +import {AuthorityOrganizationDetailTab} from '../authority-organization-detail-page/authority-organization-detail-page.model'; import { ApproveOrganization, DeactivateUser, @@ -82,6 +83,7 @@ export class AuthorityOrganizationDetailPageStateImpl { @Action(RefreshOrganization, {cancelUncompleted: true}) onRefreshOrganization( ctx: StateContext, + action: RefreshOrganization, ): Observable { return this.globalStateUtils.getDeploymentEnvironmentId().pipe( switchMap((deploymentEnvironmentId) => @@ -91,7 +93,9 @@ export class AuthorityOrganizationDetailPageStateImpl { ), ), Fetched.wrap({failureMessage: 'Failed loading organizations'}), - tap((organization) => this.organizationRefreshed(ctx, organization)), + tap((organization) => + this.organizationRefreshed(ctx, organization, action.cb), + ), ignoreElements(), ); } @@ -99,12 +103,16 @@ export class AuthorityOrganizationDetailPageStateImpl { private organizationRefreshed( ctx: StateContext, organization: Fetched, + cb?: () => void, ) { this.globalStateUtils.updateNestedProperty( ctx, 'organizationDetail.organization', organization, ); + if (cb) { + cb(); + } } @Action(ApproveOrganization) @@ -251,6 +259,13 @@ export class AuthorityOrganizationDetailPageStateImpl { ); } + redirectToMembersTab() { + this.slideOverService.setSlideOverViews( + {viewName: AuthorityOrganizationDetailTab.MEMBERS}, + {viewName: AuthorityOrganizationDetailTab.DETAIL}, + ); + } + @Action(DeactivateUser) onDeactivateUser( ctx: StateContext, @@ -275,13 +290,19 @@ export class AuthorityOrganizationDetailPageStateImpl { this.toast.showSuccess(`User deactivated successfully`); this.organizationUserRefreshed(ctx, Fetched.ready(data)); }), - finalize(() => - this.globalStateUtils.updateNestedProperty( + finalize(() => { + ctx.dispatch( + new RefreshOrganization(() => { + this.redirectToMembersTab(); + }), + ); + + return this.globalStateUtils.updateNestedProperty( ctx, 'openedUserDetail.busy', false, - ), - ), + ); + }), ignoreElements(), ); } @@ -310,13 +331,18 @@ export class AuthorityOrganizationDetailPageStateImpl { this.toast.showSuccess(`User re-activated successfully`); this.organizationUserRefreshed(ctx, Fetched.ready(data)); }), - finalize(() => - this.globalStateUtils.updateNestedProperty( + finalize(() => { + ctx.dispatch( + new RefreshOrganization(() => { + this.redirectToMembersTab(); + }), + ); + return this.globalStateUtils.updateNestedProperty( ctx, 'openedUserDetail.busy', false, - ), - ), + ); + }), ignoreElements(), ); }