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

Adding reject message functionality when moderator wants to reject entity #1450

Draft
wants to merge 10 commits into
base: Development
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -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.<a href='{url}' class="alert-link">({entity_type} ID: {pk}, VERSION ID:{history} )</a>"""
rejected_template = """The {entity_type} version has been rejected .<a href='{url}' class="alert-link">({entity_type} ID: {pk}, VERSION ID:{history} )</a>"""
pending_template = """The {entity_type} version is going to be reviewed by the moderator.<a href='{url}' class="alert-link">({entity_type} ID: {pk}, VERSION ID:{history} )</a>"""
rejected_template = """The {entity_type} version has been rejected .<a href='{url}' class="alert-link">({entity_type} ID: {pk}, VERSION ID:{history} )</a>"""

# 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']:
Expand Down Expand Up @@ -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
Expand All @@ -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)
2 changes: 2 additions & 0 deletions CodeListLibrary_project/clinicalcode/views/Decline.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db import transaction
from django.http.response import JsonResponse
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -19,7 +29,6 @@ class PublishModal {
});
const data = await response.json();
spinner.remove();
console.log(data);

const publishButton = [
{
Expand Down Expand Up @@ -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 {
Expand All @@ -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) {
Expand Down Expand Up @@ -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++) {
Expand All @@ -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) {
Expand All @@ -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 = `
<form method="post" id="decline-form-area" action="${this.decline_url}">
<div class="detailed-input-group fill">
<h3 class="detailed-input-group__title">Message for owner <span class="detailed-input-group__mandatory">*</span></h3>
<p class="detailed-input-group__description">The owner of entity will see message to change details</p>
<textarea class="text-area-input simple" cols="40" id="id_reject" required name="message" rows="10"></textarea>
</div>
</form>`;

return maincomponent;
}
}

domReady.finally(() => {
Expand Down