Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MOSIP:25261 - Feature: Terms & Conditions consent for users in CTK #708

Merged
merged 1 commit into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading