Skip to content

Commit

Permalink
MOSIP:25261 - Feature: Terms & Conditions consent for users in CTK (#708
Browse files Browse the repository at this point in the history
)

Signed-off-by: sudeep <sudeep.j7353@gmail.com>
  • Loading branch information
Sudeep7353 authored Mar 27, 2024
1 parent 9a3d170 commit a608748
Show file tree
Hide file tree
Showing 10 changed files with 297 additions and 5 deletions.
3 changes: 3 additions & 0 deletions src/app/app.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,6 @@ export const LOADING = 'loading';
export const SESSION_IDLE_TIMEOUT = "sessionIdleTimeout";
export const SESSION_IDLE_TIMER = 'sessionIdleTimer';
export const SESSION_IDLE_PING = 'sessionIdlePing';

//
export const TERMS_AND_CONDTIONS_TEMPLATE = 'terms_and_conditions_template';
130 changes: 130 additions & 0 deletions src/app/app.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -900,4 +900,134 @@ export default class Utils {
}
}

static isConsentGiven(dataService: DataService, resourceBundleJson: any, dialog: MatDialog, version: string) {
return new Promise((resolve, reject) => {
dataService.isConsentGiven(version).subscribe(
(response: any) => {
if (response.errors && response.errors.length > 0) {
Utils.showConsentError(resourceBundleJson, response.errors, dialog);
reject(response.errors);
} else {
resolve(response['response']);
}
},
(errors: any) => {
this.showConsentError(resourceBundleJson, errors, dialog);
reject(errors);
}
)
});
}

static getTemplate(dataService: DataService, resourceBundleJson: any, dialog: MatDialog, langCode: string, templateName: string, version: string) {
return new Promise((resolve, reject) => {
dataService.getTemplate(langCode, templateName, version).subscribe(
(response: any) => {
if (response.errors && response.errors.length > 0) {
Utils.showConsentError(resourceBundleJson, response.errors, dialog);
reject(response.errors);
} else {
resolve(response['response']['template']);
}
},
(errors: any) => {
Utils.showConsentError(resourceBundleJson, errors, dialog);
reject(errors);
}
);
});
}

static getLatestTemplateVersion(dataService: DataService, resourceBundleJson: any, dialog: MatDialog, templateName: string) {
return new Promise<string>((resolve, reject) => {
dataService.getLatestTemplateVersion(templateName).subscribe(
(response: any) => {
if (response.errors && response.errors.length > 0) {
Utils.showConsentError(resourceBundleJson, response.errors, dialog);
reject(response.errors);
} else {
resolve(response['response']);
}
},
(errors: any) => {
Utils.showConsentError(resourceBundleJson, errors, dialog);
reject(errors);
}
);
});
}

static showConsentError(
resourceBundle: any,
errorsList: any,
dialog: MatDialog,
customMsg?: string,
showErrCode?: boolean,
customErrorCode?: string
) {
const titleOnError = resourceBundle.serviceErrors['error'] ? resourceBundle.serviceErrors['error'] : 'Error';
let message = '';
if (errorsList && errorsList.length > 0) {
let error = errorsList[0];
const translatedMsg = resourceBundle.serviceErrors[error.errorCode];
if (!showErrCode) {
if (translatedMsg) {
message = translatedMsg;
} else {
message = error.message;
}
} else {
if (translatedMsg) {
message = error.errorCode
? error.errorCode + ' - ' + translatedMsg
: translatedMsg;
} else {
message = error.errorCode
? error.errorCode + ' - ' + error.message
: error.message;
}
}
}
if (customMsg) {
message = customErrorCode ? resourceBundle.serviceErrors[customErrorCode] : customMsg;
}
if (message == '') {
message = 'Unexpected error occured.';
}
const body = {
case: 'TERMS_AND_CONDITIONS_CONSENT_ERROR',
title: titleOnError,
message: message,
};
const dialogRef = dialog.open(DialogComponent, {
width: '400px',
data: body,
});
return dialogRef;
}

static showConsentPrompt(resourceBundle: any, titleKey: string, messageKey: string, dialog: MatDialog, customMsg?: string) {
let title: any;
let message: any;
if (resourceBundle && resourceBundle[titleKey] && resourceBundle[messageKey]) {
title = resourceBundle[titleKey];
message = resourceBundle[messageKey];
} else {
title = titleKey;
message = messageKey;
}
if (customMsg) {
message = message + " " + customMsg;
}
const body = {
case: 'TERMS_AND_CONDITIONS_CONSENT_ERROR',
title: title,
message: message,
};
const dialogRef = dialog.open(DialogComponent, {
width: '400px',
data: body,
});
return dialogRef;
}
}
17 changes: 16 additions & 1 deletion src/app/core/components/dialog/dialog.component.css
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,21 @@ ul.reviewMessage li:before {
margin-right: 5px;
}

.checkbox-margin {
margin-bottom: 10px;
margin-left: 10px;
}

.consent-checkbox {
margin-top: 10px;
margin-bottom: 5px;
margin-left: 10px;
}

.consent-checkbox ::ng-deep .mat-checkbox-layout {
white-space: normal;
}

@media screen and (max-width: 750px) {
.fieldContainer {
margin-left: 60px;
Expand Down Expand Up @@ -270,7 +285,7 @@ ul.reviewMessage li:before {
margin-top: 15px;
margin-bottom: 3px;
}
::ng-deep .mat-dialog-container{
:not(.consent-box) ::ng-deep .mat-dialog-container{
display: flex;
justify-content: center;
}
Expand Down
30 changes: 30 additions & 0 deletions src/app/core/components/dialog/dialog.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -328,4 +328,34 @@ <h2 mat-dialog-title class="mat-dialog-title">{{"dialogMessages.addProject"|tran
<button mat-flat-button color="accent" (click)="onOkClick()">{{ 'dialogMessages.close' | translate }}</button>
</mat-dialog-actions>
</div>
<div *ngIf="input.case === 'TERMS_AND_CONDITIONS_CONSENT'" class="consent-box">
<h2 mat-dialog-title class="mat-dialog-title">{{'dialogMessages.consent'|translate}}</h2>
<div class="spinner" *ngIf="!dataLoaded">
<mat-spinner diameter="50"></mat-spinner>
</div>
<div *ngIf="dataLoaded">
<div mat-dialog-content>
<div [innerHTML]="consentTemplate"></div>
</div>
</div>
<div class="consent-checkbox">
<mat-checkbox [(ngModel)]="consentCheckbox" [disabled]="!dataLoaded">{{'dialogMessages.consentCheckbox'|translate}}</mat-checkbox>
</div>
<div class="button-container-box">
<button mat-flat-button (click)="closeConsentDialog()" class="back-button">{{'dialogMessages.close'|translate}}</button>
<button mat-flat-button color="accent" [disabled]="!consentCheckbox"
(click)="setConsent()">{{'dialogMessages.confirm'|translate}}</button>
</div>
</div>
<div *ngIf="input.case === 'TERMS_AND_CONDITIONS_CONSENT_ERROR'">
<h2 mat-dialog-title class="mat-dialog-title" *ngIf="input && input.title">{{ input.title }}</h2>
<mat-dialog-content style="white-space: pre-line;">
<div class="custom-class">
<mat-icon [ngStyle]="{color: 'red'}">clear</mat-icon> {{ input.message }}
</div>
</mat-dialog-content>
<mat-dialog-actions align="end">
<button mat-flat-button color="accent" (click)="onOkClick()">{{"dialogMessages.close" | translate}}</button>
</mat-dialog-actions>
</div>
</div>
39 changes: 39 additions & 0 deletions src/app/core/components/dialog/dialog.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ export class DialogComponent implements OnInit {
adminRejectComments: string = '';
rejectReport: boolean = false;
isAndroidAppMode = environment.isAndroidAppMode == 'yes' ? true : false;
consentCheckbox: boolean;
consentTemplate: string;

constructor(
private router: Router,
Expand All @@ -80,6 +82,9 @@ export class DialogComponent implements OnInit {
this.resourceBundleJson = await Utils.getResourceBundle(this.userProfileService.getUserPreferredLanguage(), this.dataService);
this.projectId = this.input.id;
this.projectType = this.input.projectType;
if (this.input.case == 'TERMS_AND_CONDITIONS_CONSENT'){
this.consentTemplate = this.input.consentTemplate;
}
if(this.projectId) {
if (this.projectType == appConstants.SBI) {
this.initSbiProjectForm();
Expand Down Expand Up @@ -490,4 +495,38 @@ export class DialogComponent implements OnInit {
this.dialogRef.close();
this.logoutservice.logout();
}

setConsent() {
return new Promise((resolve, reject) => {
const subscription = this.dataService.setConsent().subscribe(
(response: any) => {
this.dialogRef.close();
if (response.errors && response.errors.length > 0) {
Utils.showErrorMessage(this.resourceBundleJson, response.errors, this.dialog);
resolve(false);
} else {
const msg = 'addConsentDataSuccessMsg';
const resourceBundle = this.resourceBundleJson.dialogMessages;
const successMsg = 'success';
Utils.showSuccessMessage(resourceBundle, successMsg, msg, this.dialog);
resolve(true);
}
},
(errors) => {
this.dialogRef.close();
Utils.showErrorMessage(this.resourceBundleJson, errors, this.dialog);
resolve(false);
}
);
this.subscriptions.push(subscription);
});
}

async closeConsentDialog() {
this.dialogRef.close();
const msg = 'consentNotApproved';
const resourceBundle = this.resourceBundleJson.dialogMessages;
const consentMsg = 'consent';
Utils.showConsentPrompt(resourceBundle, consentMsg, msg, this.dialog);
}
}
20 changes: 20 additions & 0 deletions src/app/core/services/data-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,26 @@ export class DataService {
return this.httpClient.get(url, { responseType: 'blob' });
}

getTemplate(langCode: string, templateName: string, version: string) {
const url = `${this.SERVICES_BASE_URL}getTemplate?langCode=${langCode}&templateName=${templateName}&version=${version}`;
return this.httpClient.get(url);
}

getLatestTemplateVersion(templateName: string) {
const url = `${this.SERVICES_BASE_URL}getLatestTemplateVersion?templateName=${templateName}`;
return this.httpClient.get(url);
}

setConsent() {
const url = `${this.SERVICES_BASE_URL}setConsent`;
return this.httpClient.post(url, {});
}

isConsentGiven(version: string) {
let url = `${this.SERVICES_BASE_URL}isConsentGiven?version=${version}`;
return this.httpClient.get(url);
}

getEncryptionKey() {
let url = `${this.SERVICES_BASE_URL}getEncryptionKey`;
return this.httpClient.get(url);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,32 @@ export class ProjectsDashboardComponent implements OnInit {
this.sort.sort(({ id: 'lastRunDt', start: 'desc'}) as MatSortable);
}
this.dataSource.sort = this.sort;
this.initConsent();
this.dataLoaded = true;
this.sessionIdleTimeout();
}

async initConsent() {
let langCode = this.userProfileService.getUserPreferredLanguage();
let templateName = appConstants.TERMS_AND_CONDTIONS_TEMPLATE;
try {
let latestTemplateVersion = await Utils.getLatestTemplateVersion(this.dataService, this.resourceBundleJson, this.dialog, templateName);
let isConsentGiven = await Utils.isConsentGiven(this.dataService, this.resourceBundleJson, this.dialog, latestTemplateVersion);
if (!isConsentGiven) {
let template = await Utils.getTemplate(this.dataService, this.resourceBundleJson, this.dialog, langCode, templateName, latestTemplateVersion);
const dialogRef = this.dialog.open(DialogComponent, {
width: '600px',
data: {
case: "TERMS_AND_CONDITIONS_CONSENT",
consentTemplate: template,
},
});
}
} catch (errors: any) {
console.error(errors[0].message);
}
}

sessionIdleTimeout() {
const subs = this.sessionLogoutService.currentMessageAutoLogout.subscribe(
(message) => (this.message = message)
Expand Down
13 changes: 12 additions & 1 deletion src/assets/i18n/ara.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@
"verifyMessage": "تحقق من قيم التجزئة وموقع الويب قبل المتابعة. إذا كانت القيم صحيحة، فاحفظ المشروع.",
"saveBtn": "يحفظ",
"sessionInactiveMessage": "سيتم مهلة لك بسبب عدم النشاط. الرجاء النقر في أي مكان لإعادة تنشيط جلستك",
"sessionInactivityLogoutMessage": "لقد تم تسجيل الخروج بسبب عدم النشاط."
"sessionInactivityLogoutMessage": "لقد تم تسجيل الخروج بسبب عدم النشاط.",
"consentNotApproved": "غير قادر على المتابعة دون موافقة الموافقة.",
"consentCheckbox": "أوافق على البنود والشروط",
"confirm": "يتأكد",
"consent": "موافقة",
"addConsentDataSuccessMsg": "لقد تم استلام موافقتك بنجاح."
},
"breadcrumb": {
"home": "بيت",
Expand Down Expand Up @@ -2023,6 +2028,10 @@
"TOOLKIT_REQ_ERR_048": "اسم المشروع",
"TOOLKIT_REQ_ERR_049": "رابط الموقع",
"TOOLKIT_REQ_ERR_050": "عنوان URL لـ activeMQ",
"TOOLKIT_REQ_ERR_051": "لم يتم العثور على القالب. قم بتحميل القالب وحاول مرة أخرى!",
"TOOLKIT_REQ_ERR_052": "حدث خطأ أثناء جلب القالب",
"TOOLKIT_REQ_ERR_053": "تنسيق إصدار القالب غير صالح.",
"TOOLKIT_REQ_ERR_054": "حدث خطأ أثناء جلب نسخة القالب.",
"TOOLKIT_REQ_ERR_500": "خطأ تقني",
"TOOLKIT_DB_ERR_001": "لقد سبق لك إنشاء مشروع بالاسم",
"TOOLKIT_DB_ERR_002": "لقد أضفت سابقًا بيانات اختبار القياسات الحيوية بالاسم",
Expand Down Expand Up @@ -2061,6 +2070,8 @@
"TOOLKIT_REPORT_004": "حدث خطأ أثناء تغيير حالة التقرير",
"TOOLKIT_REPORT_005": "حدث خطأ أثناء جلب قائمة التقارير.",
"TOOLKIT_REPORT_006": "حدث خطأ أثناء جلب قائمة التقارير. حالة التقرير غير صالحة: ",
"TOOLKIT_CONSENT_ERR_001": "حدث خطأ أثناء جلب موافقة الشريك.",
"TOOLKIT_CONSENT_ERR_002": "حدث خطأ أثناء حفظ موافقة الشريك.",
"error": "خطأ",
"FILE_WITH_MULTIPLE_EXTENSIONS": "يجب ألا يحتوي اسم الملف على امتدادات متعددة"
}
Expand Down
13 changes: 12 additions & 1 deletion src/assets/i18n/eng.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@
"verifyMessage": "Verify the Hash and Website values before proceeding. If the values are correct, save the project.",
"saveBtn": "Save",
"sessionInactiveMessage": "You will be timed out due to inactivity. Please click anywhere to re-activate your session",
"sessionInactivityLogoutMessage": "You have been logged out due to inactivity."
"sessionInactivityLogoutMessage": "You have been logged out due to inactivity.",
"consentNotApproved": "Unable to proceed without consent approval.",
"consentCheckbox": "I agree to terms and conditions",
"confirm": "Confirm",
"consent": "Consent",
"addConsentDataSuccessMsg": "Your Consent has been successfully received."
},
"breadcrumb": {
"home": "Home",
Expand Down Expand Up @@ -2023,6 +2028,10 @@
"TOOLKIT_REQ_ERR_048": "project name",
"TOOLKIT_REQ_ERR_049": "website URL",
"TOOLKIT_REQ_ERR_050": "active MQ URL",
"TOOLKIT_REQ_ERR_051": "Template not found. Upload template and try again!",
"TOOLKIT_REQ_ERR_052": "Error while fetching template",
"TOOLKIT_REQ_ERR_053": "Invalid template version format.",
"TOOLKIT_REQ_ERR_054": "Error while fetching template version.",
"TOOLKIT_DB_ERR_001": "You have previously created a project with the same name",
"TOOLKIT_DB_ERR_002": "You have previously added biometric test data with the same name",
"TOOLKIT_DB_ERR_003": "You have previously created a collection with the same name",
Expand Down Expand Up @@ -2061,6 +2070,8 @@
"TOOLKIT_REPORT_004": "Error while changing report status",
"TOOLKIT_REPORT_005": "Error while fetching list of reports",
"TOOLKIT_REPORT_006": "Error while fetching list of reports. Invalid report status: ",
"TOOLKIT_CONSENT_ERR_001": "Error while fetching partner consent.",
"TOOLKIT_CONSENT_ERR_002": "Error while saving partner consent.",
"error": "Error",
"FILE_WITH_MULTIPLE_EXTENSIONS": "File name should not contain multiple extensions"
}
Expand Down
Loading

0 comments on commit a608748

Please sign in to comment.