diff --git a/CodeListLibrary_project/clinicalcode/entity_utils/publish_utils.py b/CodeListLibrary_project/clinicalcode/entity_utils/publish_utils.py index f95631658..ae5655ff5 100644 --- a/CodeListLibrary_project/clinicalcode/entity_utils/publish_utils.py +++ b/CodeListLibrary_project/clinicalcode/entity_utils/publish_utils.py @@ -41,15 +41,15 @@ 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} )""" + 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, rejected_template) + 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']: @@ -251,10 +251,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 @@ -266,15 +266,15 @@ def send_email_decision_entity(request, entity, entity_history_id, entity_type,d 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) 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) 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..8d4669455 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,15 @@ +/** + * 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) { + constructor(publish_url, decline_url, redirect_url) { this.publish_url = publish_url; this.decline_url = decline_url; this.redirect_url = redirect_url; @@ -19,7 +29,6 @@ class PublishModal { }); const data = await response.json(); spinner.remove(); - console.log(data); const publishButton = [ { @@ -57,34 +66,18 @@ 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); } } + /** + * 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 { @@ -100,21 +93,41 @@ 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) => response.json()) + .then((response) => { + if (!response || !response?.success) { + window.ToastFactory.push({ + type: response?.approval_status == 3 ? 'danger' : 'success', + message: response?.message, + duration: 5000, + }); + return; + } + }) + .finally(() => { + spinner.remove(); + setTimeout(() => { + window.location.href = this.redirect_url + "?eraseCache=true"; + }, 5000); + }); } catch (error) { spinner.remove(); console.log(error); } } + /** + * 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) { @@ -144,6 +157,82 @@ 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", + 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.declineEntity(data); + } else { + await this.postData(data, this.publish_url); + } + }) + .catch((result) => { + if (!(result instanceof ModalFactory.ModalResults)) { + return console.error(result); + } + }); + } + + /** + * 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", + title: `Explanation for rejection ${data.name} - ${data.entity_id}?`, + content: this.generateDeclineMessage(), + beforeAccept: (modal) => { + const form = modal.querySelector("#decline-form-area"); + const textField = modal.querySelector("#id_reject"); + data.rejectMessage = 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 this.postData(data, result.data.action) + }) + .catch((e) => { + console.warn(e); + }); + }; + + /** + * 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++) { @@ -167,6 +256,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) { @@ -182,6 +276,23 @@ 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 = ` +
+
+

Message for owner *

+

The owner of entity will see message to change details

+ +
+
`; + + return maincomponent; + } } domReady.finally(() => {