Skip to content

Commit

Permalink
chore: add guard to show confirm message
Browse files Browse the repository at this point in the history
  • Loading branch information
Benmuiruri committed Oct 5, 2024
1 parent 7a0ef96 commit 1c98c4e
Show file tree
Hide file tree
Showing 14 changed files with 88 additions and 3 deletions.
5 changes: 5 additions & 0 deletions webapp/src/ts/actions/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export const Actions = {
closeSidebarMenu: createAction('CLOSE_SIDEBAR_MENU'),
openSidebarMenu: createAction('OPEN_SIDEBAR_MENU'),
setSearchBar: createSingleValueAction('SET_SEARCH_BAR', 'searchBar'),
setTrainingCard: createSingleValueAction('SET_TRAINING_CARD', 'trainingCard'),
};

export class GlobalActions {
Expand Down Expand Up @@ -96,6 +97,10 @@ export class GlobalActions {
return this.store.dispatch(Actions.setSearchBar(searchBar));
}

setTrainingCard(trainingCard) {
return this.store.dispatch(Actions.setTrainingCard(trainingCard));
}

clearSidebarFilter() {
return this.store.dispatch(Actions.clearSidebarFilter());
}
Expand Down
2 changes: 2 additions & 0 deletions webapp/src/ts/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ _.templateSettings.interpolate = /\{\{(.+?)\}\}/g;

import { AppRoutingModule } from './app-routing.module';
import { AppRouteGuardProvider } from './app-route.guard.provider';
import { TrainingCardDeactivationGuardProvider } from './training-card.guard.provider';
import { AppComponent } from './app.component';
import { ModulesModule } from '@mm-modules/modules.module';
import { environment } from '@mm-environments/environment';
Expand Down Expand Up @@ -93,6 +94,7 @@ export class MissingTranslationHandlerLog implements MissingTranslationHandler {
providers: [
{ provide: APP_BASE_HREF, useValue: '/' },
AppRouteGuardProvider,
TrainingCardDeactivationGuardProvider,
AnalyticsRouteGuardProvider,
CookieService,
ParseProvider,
Expand Down
18 changes: 16 additions & 2 deletions webapp/src/ts/modals/training-cards/training-cards.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Component, NgZone, OnDestroy, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { combineLatest, Subscription } from 'rxjs';
import { MatDialogRef } from '@angular/material/dialog';
import { Router } from '@angular/router';

import { XmlFormsService } from '@mm-services/xml-forms.service';
import { FormService } from '@mm-services/form.service';
Expand All @@ -21,6 +22,7 @@ export class TrainingCardsComponent implements OnInit, OnDestroy {
constructor(
private ngZone: NgZone,
private store: Store,
private router: Router,
private xmlFormsService: XmlFormsService,
private formService: FormService,
private geolocationService: GeolocationService,
Expand Down Expand Up @@ -51,12 +53,14 @@ export class TrainingCardsComponent implements OnInit, OnDestroy {
enketoStatus;
enketoSaving;
showConfirmExit;
nextUrl;
subscription: Subscription = new Subscription();

ngOnInit() {
this.trackRender = this.performanceService.track();
this.reset();
this.subscribeToStore();
this.globalActions.setTrainingCard({ isOpen: true });
}

ngOnDestroy() {
Expand Down Expand Up @@ -113,15 +117,19 @@ export class TrainingCardsComponent implements OnInit, OnDestroy {
this.store.select(Selectors.getEnketoSavingStatus),
this.store.select(Selectors.getEnketoError),
this.store.select(Selectors.getTrainingCardFormId),
this.store.select(Selectors.getTrainingCard),
]).subscribe(([
enketoStatus,
enketoSaving,
enketoError,
trainingCardFormId,
trainingCard,
]) => {
this.enketoStatus = enketoStatus;
this.enketoSaving = enketoSaving;
this.enketoError = enketoError;
this.showConfirmExit = trainingCard.showConfirmExit;
this.nextUrl = trainingCard.nextUrl;

if (trainingCardFormId && trainingCardFormId !== this.trainingCardFormId) {
this.trainingCardFormId = trainingCardFormId;
Expand Down Expand Up @@ -155,6 +163,7 @@ export class TrainingCardsComponent implements OnInit, OnDestroy {

close() {
this.globalActions.setTrainingCardFormId(null);
this.globalActions.setTrainingCard({ isOpen: false, showConfirmExit: false, nextUrl: null });
this.matDialogRef.close();
}

Expand Down Expand Up @@ -222,8 +231,13 @@ export class TrainingCardsComponent implements OnInit, OnDestroy {
this.showConfirmExit = confirm;
}

quitTraining() {
this.recordPerformanceQuitTraining();
quitTraining() {
this.globalActions.setTrainingCardFormId(null);

if (this.nextUrl) {
this.router.navigateByUrl(this.nextUrl);
}

this.close();
}
}
2 changes: 2 additions & 0 deletions webapp/src/ts/modules/analytics/analytics.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
AnalyticsTargetAggregatesDetailComponent
} from '@mm-modules/analytics/analytics-target-aggregates-detail.component';
import { AnalyticsRouteGuardProvider } from '@mm-modules/analytics/analytics-route.guard.provider';
import { TrainingCardDeactivationGuardProvider } from 'src/ts/training-card.guard.provider';
import { AGGREGATE_TARGETS_ID } from '@mm-services/analytics-modules.service';

export const routes:Routes = [
Expand All @@ -18,6 +19,7 @@ export const routes:Routes = [
data: { permissions: [ 'can_view_analytics' ], tab: 'analytics' },
canActivate: [ AppRouteGuardProvider ],
canActivateChild: [ AnalyticsRouteGuardProvider ],
canDeactivate: [TrainingCardDeactivationGuardProvider],
children: [
{
path: '',
Expand Down
2 changes: 2 additions & 0 deletions webapp/src/ts/modules/contacts/contacts.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ContactsContentComponent } from '@mm-modules/contacts/contacts-content.
import { ContactsDeceasedComponent } from '@mm-modules/contacts/contacts-deceased.component';
import { ContactsEditComponent } from '@mm-modules/contacts/contacts-edit.component';
import { ContactRouteGuardProvider } from '@mm-modules/contacts/contact-route.guard.provider';
import { TrainingCardDeactivationGuardProvider } from 'src/ts/training-card.guard.provider';
import { ContactsReportComponent } from '@mm-modules/contacts/contacts-report.component';

export const routes: Routes = [
Expand All @@ -14,6 +15,7 @@ export const routes: Routes = [
component: ContactsComponent,
data: {permissions: ['can_view_contacts'], tab: 'contacts'},
canActivate: [AppRouteGuardProvider],
canDeactivate: [TrainingCardDeactivationGuardProvider],
children: [
{
path: '',
Expand Down
2 changes: 2 additions & 0 deletions webapp/src/ts/modules/messages/messages.routes.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Routes, UrlSegment } from '@angular/router';

import { AppRouteGuardProvider } from '../../app-route.guard.provider';
import { TrainingCardDeactivationGuardProvider } from 'src/ts/training-card.guard.provider';
import { MessagesComponent } from './messages.component';
import { MessagesContentComponent } from './messages-content.component';

Expand All @@ -10,6 +11,7 @@ export const routes: Routes = [
component: MessagesComponent,
data: { permissions: ['can_view_messages'], tab: 'messages'},
canActivate: [AppRouteGuardProvider],
canDeactivate: [TrainingCardDeactivationGuardProvider],
children: [
{
path: '',
Expand Down
2 changes: 2 additions & 0 deletions webapp/src/ts/modules/reports/reports.routes.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Routes } from '@angular/router';

import { AppRouteGuardProvider } from '../../app-route.guard.provider';
import { TrainingCardDeactivationGuardProvider } from 'src/ts/training-card.guard.provider';
import { ReportsComponent } from '@mm-modules/reports/reports.component';
import { ReportsContentComponent } from '@mm-modules/reports/reports-content.component';
import { ReportsAddComponent } from '@mm-modules/reports/reports-add.component';
Expand All @@ -15,6 +16,7 @@ export const routes:Routes = [
component: ReportsComponent,
data: { permissions: ['can_view_reports'], tab: 'reports' },
canActivate: [AppRouteGuardProvider],
canDeactivate: [TrainingCardDeactivationGuardProvider],
children: [
{
path: '',
Expand Down
2 changes: 2 additions & 0 deletions webapp/src/ts/modules/tasks/tasks.routes.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Routes } from '@angular/router';

import { AppRouteGuardProvider } from '../../app-route.guard.provider';
import { TrainingCardDeactivationGuardProvider } from 'src/ts/training-card.guard.provider';
import { TasksComponent } from '@mm-modules/tasks/tasks.component';
import { TasksContentComponent } from '@mm-modules/tasks/tasks-content.component';
import {
Expand All @@ -15,6 +16,7 @@ export const routes:Routes = [
component: TasksComponent,
data: { permissions: ['can_edit', 'can_view_tasks'], tab: 'tasks' },
canActivate: [AppRouteGuardProvider],
canDeactivate: [TrainingCardDeactivationGuardProvider],
children: [
{
path: '',
Expand Down
11 changes: 11 additions & 0 deletions webapp/src/ts/reducers/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const initialState: GlobalState = {
filters: {}, // Selected criteria to filter data.
sidebarFilter: {},
searchBar: { isOpen: false },
trainingCard: { isOpen: false, showConfirmExit: false, nextUrl: '' },
sidebarMenu: { isOpen: false },
forms: null,
lastChangedDoc: false,
Expand Down Expand Up @@ -173,6 +174,9 @@ const _globalReducer = createReducer(
on(Actions.setSearchBar, (state, { payload: { searchBar } }) => {
return { ...state, searchBar: { ...state.searchBar, ...searchBar } };
}),
on(Actions.setTrainingCard, (state, { payload: { trainingCard } }) => {
return { ...state, trainingCard: { ...state.trainingCard, ...trainingCard } };
}),
);

export const globalReducer = (state, action) => {
Expand All @@ -189,6 +193,7 @@ export interface GlobalState {
filters: Record<string, any>; // Selected criteria to filter data.
sidebarFilter: SidebarFilterState;
searchBar: SearchBarState;
trainingCard: TrainingCardState;
sidebarMenu: SidebarMenuState;
forms: null | Record<string, any>[];
lastChangedDoc: boolean | Record<string, any>;
Expand Down Expand Up @@ -216,6 +221,12 @@ interface SearchBarState {
isOpen: boolean;
}

interface TrainingCardState {
isOpen: boolean;
showConfirmExit: boolean;
nextUrl: null|string;
}

interface SnackbarState {
message?: string;
action?: {
Expand Down
1 change: 1 addition & 0 deletions webapp/src/ts/selectors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export const Selectors = {
getUserFacilityIds: createSelector(getGlobalState, (globalState) => globalState.userFacilityIds),
getUserContactId: createSelector(getGlobalState, (globalState) => globalState.userContactId),
getTrainingCardFormId: createSelector(getGlobalState, (globalState) => globalState.trainingCardFormId),
getTrainingCard: createSelector(getGlobalState, (globalState) => globalState.trainingCard),

// enketo
getEnketoStatus: createSelector(getGlobalState, (globalState) => globalState.enketoStatus),
Expand Down
1 change: 1 addition & 0 deletions webapp/src/ts/services/modal.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export class ModalService {
return this.matDialog.open(component, {
autoFocus: false,
disableClose: true,
closeOnNavigation: true,
minWidth: isMobile ? '90vw' : '400px', // Give maximum space to date picker's calendar when in mobile.
width: '600px',
maxWidth: '90vw',
Expand Down
2 changes: 1 addition & 1 deletion webapp/src/ts/services/training-cards.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export class TrainingCardsService {
if (routeSnapshot?.data?.hideTraining) {
return;
}
this.modalService.show(TrainingCardsComponent);
this.modalService.show(TrainingCardsComponent, { closeOnNavigation: false });
}

private async getFirstChronologicalForm(xForms) {
Expand Down
40 changes: 40 additions & 0 deletions webapp/src/ts/training-card.guard.provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { ActivatedRouteSnapshot, CanDeactivate, RouterStateSnapshot } from '@angular/router';
import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { GlobalActions } from '@mm-actions/global';
import { Selectors } from '@mm-selectors/index';

@Injectable({
providedIn: 'root'
})
export class TrainingCardDeactivationGuardProvider implements CanDeactivate<any> {
private globalActions;


constructor(private store: Store) {
this.globalActions = new GlobalActions(store);
}

canDeactivate(
component: any,
currentRoute: ActivatedRouteSnapshot,
currentState: RouterStateSnapshot,
nextState: RouterStateSnapshot,
) {
let trainingCardOpen = false;

this.store
.select(Selectors.getTrainingCard)
.subscribe(trainingCard => {
trainingCardOpen = trainingCard.isOpen;
}).unsubscribe();

if (trainingCardOpen) {
this.globalActions.setTrainingCard({ isOpen: true, showConfirmExit: true, nextUrl: nextState.url });
return false;
}

return true;
}
}

1 change: 1 addition & 0 deletions webapp/tests/karma/ts/selectors/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const globalState: GlobalState = {
filterCount: { total: 5, placeFilter: 3, formFilter: 2 },
},
searchBar: { isOpen: false },
trainingCard: { isOpen: false, showConfirmExit: false, nextUrl: '' },
trainingCardFormId: 'training:new_change',
navigation: {
cancelCallback: function() {},
Expand Down

0 comments on commit 1c98c4e

Please sign in to comment.