From bd038e32fe4f7187e44da8651a328b3514dd3ef9 Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 26 Oct 2023 17:24:36 +0100 Subject: [PATCH 1/9] some rearanges and adding decline modal --- .../forms/clinical/entityPublish.js | 130 +++++++++++++----- 1 file changed, 97 insertions(+), 33 deletions(-) diff --git a/CodeListLibrary_project/cll/static/js/clinicalcode/forms/clinical/entityPublish.js b/CodeListLibrary_project/cll/static/js/clinicalcode/forms/clinical/entityPublish.js index 628501de6..b09423572 100644 --- a/CodeListLibrary_project/cll/static/js/clinicalcode/forms/clinical/entityPublish.js +++ b/CodeListLibrary_project/cll/static/js/clinicalcode/forms/clinical/entityPublish.js @@ -1,5 +1,5 @@ class PublishModal { - constructor(publish_url, decline_url,redirect_url) { + constructor(publish_url, decline_url, redirect_url) { this.publish_url = publish_url; this.decline_url = decline_url; this.redirect_url = redirect_url; @@ -57,29 +57,7 @@ class PublishModal { }, ]; - ModalFactory.create({ - id: "publish-dialog", - title: this.generateTitle(data), - content: data.errors - ? this.generateErrorContent(data) - : this.generateContent(data), - buttons: data.approval_status === 1 ? declineButton : publishButton, - }) - .then(async (result) => { - const name = result.name; - if (name == "Decline") { - await this.postData(data, this.decline_url); - window.location.href = this.redirect_url+'?eraseCache=true'; - } else { - await this.postData(data, this.publish_url); - window.location.href = this.redirect_url+'?eraseCache=true'; - } - }) - .catch((result) => { - if (!(result instanceof ModalFactory.ModalResults)) { - return console.error(result); - } - }); + this.createPublishModal(data, declineButton, publishButton); } catch (error) { console.error(error); } @@ -100,15 +78,16 @@ class PublishModal { "Cache-Control": "no-cache", }, body: JSON.stringify(data), - }).then(response => { - if (!response.ok) { - return Promise.reject(response); - } - return response.json(); - }).finally(() => { - spinner.remove(); - }); - + }) + .then((response) => { + if (!response.ok) { + return Promise.reject(response); + } + return response.json(); + }) + .finally(() => { + spinner.remove(); + }); } catch (error) { spinner.remove(); console.log(error); @@ -144,6 +123,77 @@ class PublishModal { return paragraph; } + createPublishModal(data, declineButton, publishButton) { + ModalFactory.create({ + id: "publish-dialog", + title: this.generateTitle(data), + content: data.errors + ? this.generateErrorContent(data) + : this.generateContent(data), + buttons: data.approval_status === 1 ? declineButton : publishButton, + }) + .then(async (result) => { + const name = result.name; + if (name == "Decline") { + this.declinePhenotype(data.entity_id); + } else { + await this.postData(data, this.publish_url); + window.location.href = this.redirect_url + "?eraseCache=true"; + } + }) + .catch((result) => { + if (!(result instanceof ModalFactory.ModalResults)) { + return console.error(result); + } + }); + } + + declinePhenotype = (id) => { + window.ModalFactory.create({ + id: 'decline-dialog', + title: `Are you sure you want to archive ${id}?`, + content: this.generateDeclineMessage(), + onRender: (modal) => { + const entityIdField = modal.querySelector('#id_entity_id'); + entityIdField.value = id; + + const passphraseField = modal.querySelector('#id_passphrase'); + passphraseField.setAttribute('placeholder', id); + }, + beforeAccept: (modal) => { + const form = modal.querySelector('#decline-form-area'); + const textField = modal.querySelector('#id_reject'); + console.log(textField.value); + return { + form: new FormData(form), + action: form.action, + }; + } + }) + .then((result) => { + return fetch(result.data.action, { + method: 'post', + body: result.data.form, + }) + .then(response => response.json()) + .then(response => { + if (!response || !response?.success) { + window.ToastFactory.push({ + type: 'warning', + message: response?.message || 'Form Error', + duration: 5000, + }); + return; + } + + window.location.reload(); + }); + }) + .catch((e) => { + console.warn(e); + }); + } + generateErrorContent(data) { let errorsHtml = ""; for (let i = 0; i < data.errors.length; i++) { @@ -182,6 +232,18 @@ class PublishModal { } return title; } + + generateDeclineMessage() { + let maincomponent = ` +
+
+

Message for owner

+ +
+
`; + + return maincomponent; + } } domReady.finally(() => { @@ -193,4 +255,6 @@ domReady.finally(() => { url_decline.innerHTML, redirect_url.innerHTML ); + + }); From 9342dd1b64be4bf11820ba14e66193064604a2b2 Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 27 Oct 2023 11:07:04 +0100 Subject: [PATCH 2/9] Adding validation for textfield --- .../forms/clinical/entityPublish.js | 61 +++++++------------ 1 file changed, 23 insertions(+), 38 deletions(-) diff --git a/CodeListLibrary_project/cll/static/js/clinicalcode/forms/clinical/entityPublish.js b/CodeListLibrary_project/cll/static/js/clinicalcode/forms/clinical/entityPublish.js index b09423572..a8127738c 100644 --- a/CodeListLibrary_project/cll/static/js/clinicalcode/forms/clinical/entityPublish.js +++ b/CodeListLibrary_project/cll/static/js/clinicalcode/forms/clinical/entityPublish.js @@ -135,7 +135,7 @@ class PublishModal { .then(async (result) => { const name = result.name; if (name == "Decline") { - this.declinePhenotype(data.entity_id); + this.declinePhenotype(data); } else { await this.postData(data, this.publish_url); window.location.href = this.redirect_url + "?eraseCache=true"; @@ -148,52 +148,39 @@ class PublishModal { }); } - declinePhenotype = (id) => { + declineEntity = (data) => { window.ModalFactory.create({ - id: 'decline-dialog', - title: `Are you sure you want to archive ${id}?`, + id: "decline-dialog", + title: `Explanation for rejection ${data.name} - ${data.entity_id}?`, content: this.generateDeclineMessage(), - onRender: (modal) => { - const entityIdField = modal.querySelector('#id_entity_id'); - entityIdField.value = id; - - const passphraseField = modal.querySelector('#id_passphrase'); - passphraseField.setAttribute('placeholder', id); - }, beforeAccept: (modal) => { - const form = modal.querySelector('#decline-form-area'); - const textField = modal.querySelector('#id_reject'); - console.log(textField.value); + const form = modal.querySelector("#decline-form-area"); + const textField = modal.querySelector("#id_reject"); + data.message = textField.value; + if (textField.value.trim() === "") { + window.ToastFactory.push({ + type: "warning", + message: "Please provide an explanation for the rejection.", + duration: 5000, + }); + return false; + } return { form: new FormData(form), action: form.action, }; - } + }, }) .then((result) => { - return fetch(result.data.action, { - method: 'post', - body: result.data.form, - }) - .then(response => response.json()) - .then(response => { - if (!response || !response?.success) { - window.ToastFactory.push({ - type: 'warning', - message: response?.message || 'Form Error', - duration: 5000, - }); - return; - } - - window.location.reload(); - }); + return this.postData(data, result.data.action).then((response) => { + window.location.href = this.redirect_url + "?eraseCache=true"; + }); }) .catch((e) => { console.warn(e); }); - } - + }; + generateErrorContent(data) { let errorsHtml = ""; for (let i = 0; i < data.errors.length; i++) { @@ -237,8 +224,8 @@ class PublishModal { let maincomponent = `
-

Message for owner

- +

Message for owner *

+
`; @@ -255,6 +242,4 @@ domReady.finally(() => { url_decline.innerHTML, redirect_url.innerHTML ); - - }); From ac4cdc0dee4868b8599c8fad27f45d338e5b418b Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 27 Oct 2023 14:11:30 +0100 Subject: [PATCH 3/9] adding description and changed name of function --- .../cll/static/js/clinicalcode/forms/clinical/entityPublish.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CodeListLibrary_project/cll/static/js/clinicalcode/forms/clinical/entityPublish.js b/CodeListLibrary_project/cll/static/js/clinicalcode/forms/clinical/entityPublish.js index a8127738c..6f070f965 100644 --- a/CodeListLibrary_project/cll/static/js/clinicalcode/forms/clinical/entityPublish.js +++ b/CodeListLibrary_project/cll/static/js/clinicalcode/forms/clinical/entityPublish.js @@ -135,7 +135,7 @@ class PublishModal { .then(async (result) => { const name = result.name; if (name == "Decline") { - this.declinePhenotype(data); + this.declineEntity(data); } else { await this.postData(data, this.publish_url); window.location.href = this.redirect_url + "?eraseCache=true"; @@ -225,6 +225,7 @@ class PublishModal {

Message for owner *

+

The owner of entity will see message to change details

`; From f368a269592fa91df1ce7e284c73a10ed8753a0f Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 27 Oct 2023 16:51:13 +0100 Subject: [PATCH 4/9] added custom message for backend --- .../clinicalcode/entity_utils/publish_utils.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CodeListLibrary_project/clinicalcode/entity_utils/publish_utils.py b/CodeListLibrary_project/clinicalcode/entity_utils/publish_utils.py index f95631658..a582af5af 100644 --- a/CodeListLibrary_project/clinicalcode/entity_utils/publish_utils.py +++ b/CodeListLibrary_project/clinicalcode/entity_utils/publish_utils.py @@ -41,7 +41,6 @@ def send_message(request, pk, data, entity, entity_history_id, checks): """ # Message templates approved_template = """The {entity_type} version has been successfully published.({entity_type} ID: {pk}, VERSION ID:{history} )""" - rejected_template = """The {entity_type} version has been rejected .({entity_type} ID: {pk}, VERSION ID:{history} )""" pending_template = """The {entity_type} version is going to be reviewed by the moderator.({entity_type} ID: {pk}, VERSION ID:{history} )""" # Determine the appropriate message template and send email @@ -49,7 +48,7 @@ def send_message(request, pk, data, entity, entity_history_id, checks): if approval_status == constants.APPROVAL_STATUS.APPROVED: return format_message_and_send_email(request, pk, data, entity, entity_history_id, checks, approved_template) elif approval_status == constants.APPROVAL_STATUS.REJECTED: - return format_message_and_send_email(request, pk, data, entity, entity_history_id, checks, rejected_template) + return format_message_and_send_email(request, pk, data, entity, entity_history_id, checks, data['rejectMessage']) elif approval_status == constants.APPROVAL_STATUS.PENDING: return format_message_and_send_email(request, pk, data, entity, entity_history_id, checks, pending_template) elif approval_status is None and checks['is_moderator']: @@ -251,10 +250,10 @@ def format_message_and_send_email(request, pk, data, entity, entity_history_id, pk=pk, history=entity_history_id ) - send_email_decision_entity(request,entity, entity_history_id, checks['entity_type'], data) + send_email_decision_entity(request,entity, entity_history_id, data) return data -def send_email_decision_entity(request, entity, entity_history_id, entity_type,data): +def send_email_decision_entity(request, entity, entity_history_id,data): """ Call util function to send email decision @param workingset: workingset object From 9713142f5896c799c970c30afda069222b0b57de Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 27 Oct 2023 16:51:23 +0100 Subject: [PATCH 5/9] for decline request --- CodeListLibrary_project/clinicalcode/views/Decline.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CodeListLibrary_project/clinicalcode/views/Decline.py b/CodeListLibrary_project/clinicalcode/views/Decline.py index 97c0d50fe..f31bfade5 100644 --- a/CodeListLibrary_project/clinicalcode/views/Decline.py +++ b/CodeListLibrary_project/clinicalcode/views/Decline.py @@ -1,3 +1,4 @@ +import json from django.contrib.auth.mixins import LoginRequiredMixin from django.db import transaction from django.http.response import JsonResponse @@ -53,6 +54,7 @@ def post(self, request, pk, history_id): data['form_is_valid'] = True data['approval_status'] = constants.APPROVAL_STATUS.REJECTED data['entity_name_requested'] = GenericEntity.history.get(id=pk, history_id=history_id).name + data['rejectMessage'] = json.loads(request.body)['rejectMessage'] data = publish_utils.form_validation(request, data, history_id, pk, entity, checks) except Exception as e: #print(e) From a971482452962930eeefdf6f3921eb688641801e Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 27 Oct 2023 16:56:58 +0100 Subject: [PATCH 6/9] added docs --- .../forms/clinical/entityPublish.js | 73 +++++++++++++++++-- 1 file changed, 68 insertions(+), 5 deletions(-) diff --git a/CodeListLibrary_project/cll/static/js/clinicalcode/forms/clinical/entityPublish.js b/CodeListLibrary_project/cll/static/js/clinicalcode/forms/clinical/entityPublish.js index 6f070f965..167ae31a7 100644 --- a/CodeListLibrary_project/cll/static/js/clinicalcode/forms/clinical/entityPublish.js +++ b/CodeListLibrary_project/cll/static/js/clinicalcode/forms/clinical/entityPublish.js @@ -1,3 +1,13 @@ +/** + * Represents a modal for publishing or declining an entity. + + /** + * Creates a new instance of the PublishModal class. + * @constructor + * @param {string} publish_url - The URL to publish the entity. + * @param {string} decline_url - The URL to decline the entity. + * @param {string} redirect_url - The URL to redirect to after publishing or declining the entity. + */ class PublishModal { constructor(publish_url, decline_url, redirect_url) { this.publish_url = publish_url; @@ -63,6 +73,12 @@ class PublishModal { } } + /** + * Sends a POST request to the specified URL with the provided data. + * @async + * @param {Object} data - The data to send in the request body. + * @param {string} url - The URL to send the request to. + */ async postData(data, url) { const spinner = startLoadingSpinner(); try { @@ -79,14 +95,22 @@ class PublishModal { }, body: JSON.stringify(data), }) + .then((response) => response.json()) .then((response) => { - if (!response.ok) { - return Promise.reject(response); + if (!response || !response?.success) { + window.ToastFactory.push({ + type: 'success', + message: response?.message, + duration: 5000, + }); + return; } - return response.json(); }) .finally(() => { spinner.remove(); + setTimeout(() => { + window.location.href = this.redirect_url + "?eraseCache=true"; + }, 5000); }); } catch (error) { spinner.remove(); @@ -94,6 +118,16 @@ class PublishModal { } } + /** + * Generates a paragraph with a confirmation message for publishing or approving a clinical entity. + * @param {Object} data - The data object containing information about the clinical entity. + * @param {string} data.name - The name of the clinical entity. + * @param {string} data.entity_type - The type of the clinical entity. + * @param {number|null} data.approval_status - The approval status of the clinical entity. Can be 1, 3 or null. + * @param {boolean} data.is_moderator - Indicates whether the user is a moderator. + * @param {boolean} data.is_lastapproved - Indicates whether the user is the last one to approve the clinical entity. + * @returns {string} A paragraph with a confirmation message for publishing or approving a clinical entity. + */ generateContent(data) { let paragraph; switch (data.approval_status) { @@ -123,6 +157,13 @@ class PublishModal { return paragraph; } + /** + * Creates a modal dialog for publishing an entity. + * + * @param {Object} data - The data for the entity being published. + * @param {Object} declineButton - The button to decline publishing the entity. + * @param {Object} publishButton - The button to publish the entity. + */ createPublishModal(data, declineButton, publishButton) { ModalFactory.create({ id: "publish-dialog", @@ -138,7 +179,6 @@ class PublishModal { this.declineEntity(data); } else { await this.postData(data, this.publish_url); - window.location.href = this.redirect_url + "?eraseCache=true"; } }) .catch((result) => { @@ -148,6 +188,13 @@ class PublishModal { }); } + /** + * Declines an entity and prompts the user to provide an explanation for the rejection. + * @param {Object} data - The data object containing information about the entity to be declined. + * @param {string} data.name - The name of the entity to be declined. + * @param {string} data.entity_id - The ID of the entity to be declined. + * @returns {Promise} A promise that resolves when the entity has been declined and the page has been redirected. + */ declineEntity = (data) => { window.ModalFactory.create({ id: "decline-dialog", @@ -156,7 +203,7 @@ class PublishModal { beforeAccept: (modal) => { const form = modal.querySelector("#decline-form-area"); const textField = modal.querySelector("#id_reject"); - data.message = textField.value; + data.rejectMessage = textField.value; if (textField.value.trim() === "") { window.ToastFactory.push({ type: "warning", @@ -181,6 +228,13 @@ class PublishModal { }); }; + /** + * Generates HTML content for displaying errors when an entity cannot be published. + * @param {Object} data - The data object containing the errors. + * @param {Array} data.errors - An array of error objects. + * @param {string} data.errors.url_parent - The URL of the parent entity that caused the error (if applicable). + * @returns {string} - The HTML content to display the errors. + */ generateErrorContent(data) { let errorsHtml = ""; for (let i = 0; i < data.errors.length; i++) { @@ -204,6 +258,11 @@ class PublishModal { return html; } + /** + * Generates a title based on the approval status, entity ID, entity type, and name. + * @param {Object} data - The data object containing the approval status, entity ID, entity type, and name. + * @returns {string} The generated title. + */ generateTitle(data) { let title; switch (data.approval_status) { @@ -220,6 +279,10 @@ class PublishModal { return title; } + /** + * Generates a decline message form for the owner of an entity to change details. + * @returns {string} The HTML string of the decline message form. + */ generateDeclineMessage() { let maincomponent = `
From 58adb505219a0b5087e90acca371970df95bdedc Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 30 Oct 2023 11:58:45 +0000 Subject: [PATCH 7/9] added different case --- .../clinicalcode/entity_utils/publish_utils.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CodeListLibrary_project/clinicalcode/entity_utils/publish_utils.py b/CodeListLibrary_project/clinicalcode/entity_utils/publish_utils.py index a582af5af..c148115df 100644 --- a/CodeListLibrary_project/clinicalcode/entity_utils/publish_utils.py +++ b/CodeListLibrary_project/clinicalcode/entity_utils/publish_utils.py @@ -265,15 +265,15 @@ def send_email_decision_entity(request, entity, entity_history_id,data): if data['approval_status'].value == 1: context["status"] = "Pending" - context["message"] = "submitted and is under review" + context["message"] = "Submitted and is under review" send_review_email(request, context) elif data['approval_status'].value == 2: # This line for the case when user want to get notification of same workingset id but different version context["status"] = "Published" - context["message"] = "approved and successfully published" + context["message"] = "Approved and successfully published" send_review_email(request, context) elif data['approval_status'].value == 3: context["status"] = "Rejected" - context["message"] = "rejected by the moderator" - context["custom_message"] = "Please adjust changes and try again" #TODO add custom message logic + context["message"] = "Rejected by the moderator" + context["custom_message"] = data['rejectMessage'] send_review_email(request, context) From 1bb70a4397e61f6006d425fa277ced08a6b72a08 Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 30 Oct 2023 13:37:19 +0000 Subject: [PATCH 8/9] restored message --- .../clinicalcode/entity_utils/publish_utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CodeListLibrary_project/clinicalcode/entity_utils/publish_utils.py b/CodeListLibrary_project/clinicalcode/entity_utils/publish_utils.py index c148115df..ae5655ff5 100644 --- a/CodeListLibrary_project/clinicalcode/entity_utils/publish_utils.py +++ b/CodeListLibrary_project/clinicalcode/entity_utils/publish_utils.py @@ -42,13 +42,14 @@ def send_message(request, pk, data, entity, entity_history_id, checks): # Message templates approved_template = """The {entity_type} version has been successfully published.({entity_type} ID: {pk}, VERSION ID:{history} )""" pending_template = """The {entity_type} version is going to be reviewed by the moderator.({entity_type} ID: {pk}, VERSION ID:{history} )""" + rejected_template = """The {entity_type} version has been rejected .({entity_type} ID: {pk}, VERSION ID:{history} )""" # Determine the appropriate message template and send email approval_status = data['approval_status'] if approval_status == constants.APPROVAL_STATUS.APPROVED: return format_message_and_send_email(request, pk, data, entity, entity_history_id, checks, approved_template) elif approval_status == constants.APPROVAL_STATUS.REJECTED: - return format_message_and_send_email(request, pk, data, entity, entity_history_id, checks, data['rejectMessage']) + return format_message_and_send_email(request, pk, data, entity, entity_history_id, checks,rejected_template) elif approval_status == constants.APPROVAL_STATUS.PENDING: return format_message_and_send_email(request, pk, data, entity, entity_history_id, checks, pending_template) elif approval_status is None and checks['is_moderator']: From 81cc5da05fe58e6b2648b057fc7467bfa8574c36 Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 30 Oct 2023 13:37:41 +0000 Subject: [PATCH 9/9] adding message when user decline --- .../js/clinicalcode/forms/clinical/entityPublish.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/CodeListLibrary_project/cll/static/js/clinicalcode/forms/clinical/entityPublish.js b/CodeListLibrary_project/cll/static/js/clinicalcode/forms/clinical/entityPublish.js index 167ae31a7..8d4669455 100644 --- a/CodeListLibrary_project/cll/static/js/clinicalcode/forms/clinical/entityPublish.js +++ b/CodeListLibrary_project/cll/static/js/clinicalcode/forms/clinical/entityPublish.js @@ -29,7 +29,6 @@ class PublishModal { }); const data = await response.json(); spinner.remove(); - console.log(data); const publishButton = [ { @@ -97,9 +96,10 @@ class PublishModal { }) .then((response) => response.json()) .then((response) => { + if (!response || !response?.success) { window.ToastFactory.push({ - type: 'success', + type: response?.approval_status == 3 ? 'danger' : 'success', message: response?.message, duration: 5000, }); @@ -219,9 +219,7 @@ class PublishModal { }, }) .then((result) => { - return this.postData(data, result.data.action).then((response) => { - window.location.href = this.redirect_url + "?eraseCache=true"; - }); + return this.postData(data, result.data.action) }) .catch((e) => { console.warn(e);