Merge pull request #93: Update section completion and show rule violations
Display rule violations and update section completion flag
This commit is contained in:
commit
8f290fe2ed
5 changed files with 127 additions and 44 deletions
|
@ -1,13 +0,0 @@
|
||||||
.signup
|
|
||||||
{
|
|
||||||
height: 200px;
|
|
||||||
width: 400px;
|
|
||||||
position: fixed;
|
|
||||||
left: 50%;
|
|
||||||
margin-left: -150px;
|
|
||||||
width:60%;
|
|
||||||
}
|
|
||||||
.border
|
|
||||||
{
|
|
||||||
border-color:black;
|
|
||||||
}
|
|
10
front/static/css/style.css
Normal file
10
front/static/css/style.css
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
.fa-check-square {
|
||||||
|
float: right;
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-exclamation-triangle {
|
||||||
|
float: right;
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
|
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
|
||||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous">
|
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous">
|
||||||
|
<link rel="stylesheet" href="css/style.css">
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
|
||||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script>
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/classlist/1.2.20171210/classList.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/classlist/1.2.20171210/classList.min.js"></script>
|
||||||
|
@ -53,7 +54,6 @@
|
||||||
<tr>
|
<tr>
|
||||||
<th>Title</th>
|
<th>Title</th>
|
||||||
<th>Date Created</th>
|
<th>Date Created</th>
|
||||||
<th class="d-none d-lg-table-cell">State</th>
|
|
||||||
<th class="d-none d-md-table-cell">Date Submitted</th>
|
<th class="d-none d-md-table-cell">Date Submitted</th>
|
||||||
<th>Action</th>
|
<th>Action</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -9,12 +9,6 @@ function getEndpointDomain() {
|
||||||
return "https://" + window.location.hostname + ":8444/";
|
return "https://" + window.location.hostname + ":8444/";
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveSectionCallback(parsedData, saveButton) {
|
|
||||||
alert(JSON.stringify(parsedData));
|
|
||||||
saveButton.innerHTML = "Save";
|
|
||||||
saveButton.disabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeAjaxRequest(method, url, callback, optional, payload) {
|
function makeAjaxRequest(method, url, callback, optional, payload) {
|
||||||
const token = localStorage.getItem("token");
|
const token = localStorage.getItem("token");
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
|
@ -57,6 +51,45 @@ function makeAjaxRequest(method, url, callback, optional, payload) {
|
||||||
xhr.send(payload);
|
xhr.send(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateSection(parsedData, saveButton) {
|
||||||
|
const sectionIdStr = "#section-" + parsedData.id + "-";
|
||||||
|
const sectionState = document.querySelector(sectionIdStr + "state");
|
||||||
|
const collapseDiv = document.querySelector(sectionIdStr + "collapse");
|
||||||
|
|
||||||
|
// A completed section gets a header icon
|
||||||
|
if (parsedData.completed) {
|
||||||
|
const sectionAlert = collapseDiv.querySelector(".section-alert");
|
||||||
|
console.log(sectionAlert);
|
||||||
|
if (sectionAlert) {
|
||||||
|
collapseDiv.firstChild.removeChild(sectionAlert);
|
||||||
|
}
|
||||||
|
if (parsedData.rule_violations.length === 0) {
|
||||||
|
// Complete with no rule violations
|
||||||
|
sectionState.classList = "fas fa-check-square";
|
||||||
|
collapseDiv.className = "collapse";
|
||||||
|
} else {
|
||||||
|
// Complete but with rule violations
|
||||||
|
sectionState.classList = "fas fa-exclamation-triangle";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const cardFooter = createCardFooter(parsedData.rule_violations);
|
||||||
|
if (collapseDiv.lastElementChild.classList.contains("card-footer")) {
|
||||||
|
collapseDiv.removeChild(collapseDiv.lastElementChild);
|
||||||
|
if (cardFooter) {
|
||||||
|
collapseDiv.appendChild(cardFooter);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (cardFooter) {
|
||||||
|
collapseDiv.appendChild(cardFooter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
saveButton.innerHTML = "Save";
|
||||||
|
saveButton.disabled = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Wraps a Bootstrap form group around a field
|
// Wraps a Bootstrap form group around a field
|
||||||
function createFormGroup(sectionIdStr, field) {
|
function createFormGroup(sectionIdStr, field) {
|
||||||
const inputId = sectionIdStr + field.field_name;
|
const inputId = sectionIdStr + field.field_name;
|
||||||
|
@ -164,15 +197,27 @@ function createFormGroup(sectionIdStr, field) {
|
||||||
return formGroup;
|
return formGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createCollapsibleCard(sectionIdStr, sectionTitle) {
|
function createCollapsibleCard(sectionIdStr, sectionTitle, sectionCompleted, ruleViolations) {
|
||||||
// Create card and header
|
// Create card and header
|
||||||
const card = document.createElement("div");
|
const card = document.createElement("div");
|
||||||
card.classList.add("card");
|
card.classList.add("card");
|
||||||
const cardHeader = document.createElement("div");
|
const cardHeader = document.createElement("div");
|
||||||
cardHeader.classList.add("card-header");
|
cardHeader.classList.add("card-header");
|
||||||
|
const sectionState = document.createElement("i");
|
||||||
|
sectionState.id = sectionIdStr + "state";
|
||||||
|
|
||||||
|
// A completed section gets a header icon
|
||||||
|
if (sectionCompleted) {
|
||||||
|
if (ruleViolations.length === 0) {
|
||||||
|
sectionState.classList.add("fas", "fa-check-square");
|
||||||
|
} else {
|
||||||
|
sectionState.classList.add("fas", "fa-exclamation-triangle");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create h2, button. Append button to h2, h2 to header, and header to card
|
// Create h2, button. Append button to h2, h2 to header, and header to card
|
||||||
const h2 = document.createElement("h2");
|
const h2 = document.createElement("h2");
|
||||||
|
h2.classList.add("mb-0");
|
||||||
const button = document.createElement("button");
|
const button = document.createElement("button");
|
||||||
button.classList.add("btn", "btn-link");
|
button.classList.add("btn", "btn-link");
|
||||||
button.type = "button";
|
button.type = "button";
|
||||||
|
@ -180,43 +225,75 @@ function createCollapsibleCard(sectionIdStr, sectionTitle) {
|
||||||
button.setAttribute("data-target", "#" + sectionIdStr + "collapse");
|
button.setAttribute("data-target", "#" + sectionIdStr + "collapse");
|
||||||
button.innerHTML = sectionTitle;
|
button.innerHTML = sectionTitle;
|
||||||
h2.appendChild(button);
|
h2.appendChild(button);
|
||||||
|
h2.appendChild(sectionState);
|
||||||
cardHeader.appendChild(h2);
|
cardHeader.appendChild(h2);
|
||||||
card.appendChild(cardHeader);
|
card.appendChild(cardHeader);
|
||||||
|
|
||||||
return card;
|
return card;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createCollapsibleCardBody(form, type, sectionIdStr, sectionDescription, sectionCompleted) {
|
function createCollapsibleCardBody(form, type, sectionIdStr, sectionDescription, sectionCompleted, ruleViolations) {
|
||||||
// Create wrapper div
|
// Create wrapper div
|
||||||
const div = document.createElement("div");
|
const collapseDiv = document.createElement("div");
|
||||||
div.id = sectionIdStr + "collapse";
|
collapseDiv.id = sectionIdStr + "collapse";
|
||||||
const sectionAlert = document.createElement("div");
|
|
||||||
const cardBody = document.createElement("div");
|
const cardBody = document.createElement("div");
|
||||||
cardBody.classList.add("card-body");
|
cardBody.classList.add("card-body");
|
||||||
|
const sectionAlert = document.createElement("div");
|
||||||
|
|
||||||
if (sectionCompleted) {
|
if (sectionCompleted) {
|
||||||
div.classList.add("collapse");
|
if (ruleViolations.length === 0) {
|
||||||
sectionAlert.classList.add("alert", "alert-success");
|
collapseDiv.classList.add("collapse");
|
||||||
sectionAlert.innerHTML = "This section is complete";
|
|
||||||
} else {
|
} else {
|
||||||
div.classList.add("collapse", "show");
|
collapseDiv.classList.add("collapse", "show");
|
||||||
sectionAlert.classList.add("alert", "alert-danger");
|
}
|
||||||
|
} else {
|
||||||
|
sectionAlert.classList.add("alert", "alert-danger", "section-alert");
|
||||||
sectionAlert.innerHTML = "This section is not complete";
|
sectionAlert.innerHTML = "This section is not complete";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === reportType.EDIT) {
|
if (type === reportType.EDIT) {
|
||||||
div.setAttribute("data-parent", "#editReportAccordion");
|
collapseDiv.setAttribute("data-parent", "#editReportAccordion");
|
||||||
} else {
|
} else {
|
||||||
div.setAttribute("data-parent", "#newReportAccordion");
|
collapseDiv.setAttribute("data-parent", "#newReportAccordion");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create card body. Append form to body, body to wrapper div
|
// Create card body. Append form to body, body to wrapper div
|
||||||
cardBody.appendChild(sectionAlert);
|
cardBody.appendChild(sectionAlert);
|
||||||
cardBody.insertAdjacentHTML("beforeend", sectionDescription);
|
cardBody.insertAdjacentHTML("beforeend", sectionDescription);
|
||||||
cardBody.appendChild(form);
|
cardBody.appendChild(form);
|
||||||
div.appendChild(cardBody);
|
collapseDiv.appendChild(cardBody);
|
||||||
|
|
||||||
return div;
|
return collapseDiv;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createCardFooter(ruleViolations) {
|
||||||
|
if (ruleViolations.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cardFooter = document.createElement("div");
|
||||||
|
cardFooter.classList.add("card-footer");
|
||||||
|
const violationMessage = document.createElement("div");
|
||||||
|
violationMessage.classList.add("alert", "alert-danger");
|
||||||
|
const heading = document.createElement("div");
|
||||||
|
heading.innerHTML = "Rule Violations";
|
||||||
|
heading.classList.add("alert-heading");
|
||||||
|
violationMessage.appendChild(heading);
|
||||||
|
violationMessage.appendChild(document.createElement("hr"));
|
||||||
|
|
||||||
|
for (let i = 0; i < ruleViolations.length; i++) {
|
||||||
|
let violation = document.createElement("p");
|
||||||
|
let violationLabel = document.createElement("strong");
|
||||||
|
violationLabel.innerHTML = ruleViolations[i].label;
|
||||||
|
violation.appendChild(violationLabel);
|
||||||
|
violation.appendChild(document.createElement("br"));
|
||||||
|
let ruleBreakText = document.createTextNode(ruleViolations[i].rule_break_text);
|
||||||
|
violation.appendChild(ruleBreakText);
|
||||||
|
violationMessage.appendChild(violation);
|
||||||
|
}
|
||||||
|
|
||||||
|
cardFooter.appendChild(violationMessage);
|
||||||
|
return cardFooter;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createReportForm(parsedData, type) {
|
function createReportForm(parsedData, type) {
|
||||||
|
@ -252,16 +329,14 @@ function createReportForm(parsedData, type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add report title and date
|
// Add report title and date
|
||||||
const reportTitle = parsedData.title;
|
modalLabel.innerHTML = parsedData.title;
|
||||||
modalLabel.innerHTML = reportTitle;
|
|
||||||
|
|
||||||
// Traverse the report's sections array
|
// Traverse the report's sections array
|
||||||
const sections = parsedData.sections;
|
const sections = parsedData.sections;
|
||||||
for (let i = 0; i < sections.length; i++) {
|
for (let i = 0; i < sections.length; i++) {
|
||||||
let sectionIdStr = "section-" + sections[i].id + "-";
|
|
||||||
let collapsibleCard = createCollapsibleCard(sectionIdStr, sections[i].title)
|
|
||||||
|
|
||||||
// Create a new form with the section key index as id
|
// Create a new form
|
||||||
|
let sectionIdStr = "section-" + sections[i].id + "-";
|
||||||
let form = document.createElement("form");
|
let form = document.createElement("form");
|
||||||
form.classList.add("form", "section-form");
|
form.classList.add("form", "section-form");
|
||||||
form.id = sectionIdStr + "form";
|
form.id = sectionIdStr + "form";
|
||||||
|
@ -272,9 +347,11 @@ function createReportForm(parsedData, type) {
|
||||||
let fields = sections[i].fields;
|
let fields = sections[i].fields;
|
||||||
for (let j = 0; j < fields.length; j++) {
|
for (let j = 0; j < fields.length; j++) {
|
||||||
|
|
||||||
|
/*
|
||||||
console.log("Field label: " + fields[j].label);
|
console.log("Field label: " + fields[j].label);
|
||||||
console.log("Field type: " + fields[j].field_type);
|
console.log("Field type: " + fields[j].field_type);
|
||||||
console.log("Field value: " + fields[j].value);
|
console.log("Field value: " + fields[j].value);
|
||||||
|
*/
|
||||||
|
|
||||||
// Create a form group for each field and add it to the form
|
// Create a form group for each field and add it to the form
|
||||||
form.appendChild(createFormGroup(sectionIdStr, fields[j]));
|
form.appendChild(createFormGroup(sectionIdStr, fields[j]));
|
||||||
|
@ -288,7 +365,14 @@ function createReportForm(parsedData, type) {
|
||||||
form.appendChild(saveButton);
|
form.appendChild(saveButton);
|
||||||
|
|
||||||
// Create collapsible card body, append form to it, append card to accordion
|
// Create collapsible card body, append form to it, append card to accordion
|
||||||
let cardBody = createCollapsibleCardBody(form, type, sectionIdStr, sections[i].html_description, sections[i].completed);
|
let cardBody = createCollapsibleCardBody(form, type, sectionIdStr,
|
||||||
|
sections[i].html_description, sections[i].completed, sections[i].rule_violations);
|
||||||
|
let cardFooter = createCardFooter(sections[i].rule_violations);
|
||||||
|
if (cardFooter) {
|
||||||
|
cardBody.appendChild(cardFooter);
|
||||||
|
}
|
||||||
|
let collapsibleCard = createCollapsibleCard(sectionIdStr, sections[i].title,
|
||||||
|
sections[i].completed, sections[i].rule_violations)
|
||||||
collapsibleCard.appendChild(cardBody);
|
collapsibleCard.appendChild(cardBody);
|
||||||
accordion.appendChild(collapsibleCard);
|
accordion.appendChild(collapsibleCard);
|
||||||
}
|
}
|
||||||
|
@ -323,9 +407,11 @@ function displayListOfReports(parsedData) {
|
||||||
bodyRow.insertCell(0).innerHTML = title;
|
bodyRow.insertCell(0).innerHTML = title;
|
||||||
bodyRow.insertCell(1).innerHTML = dateCreated;
|
bodyRow.insertCell(1).innerHTML = dateCreated;
|
||||||
|
|
||||||
|
/*
|
||||||
let stateCell = bodyRow.insertCell(2);
|
let stateCell = bodyRow.insertCell(2);
|
||||||
stateCell.innerHTML = state;
|
stateCell.innerHTML = state;
|
||||||
stateCell.classList.add("d-none", "d-lg-table-cell"); // Column visible only on large displays
|
stateCell.classList.add("d-none", "d-lg-table-cell"); // Column visible only on large displays
|
||||||
|
*/
|
||||||
|
|
||||||
// Create edit/view button
|
// Create edit/view button
|
||||||
let actionButton = document.createElement("button");
|
let actionButton = document.createElement("button");
|
||||||
|
@ -349,10 +435,10 @@ function displayListOfReports(parsedData) {
|
||||||
actionButton.setAttribute("data-target", "#viewReportModal");
|
actionButton.setAttribute("data-target", "#viewReportModal");
|
||||||
}
|
}
|
||||||
|
|
||||||
let dateSubmittedCell = bodyRow.insertCell(3);
|
let dateSubmittedCell = bodyRow.insertCell(2);
|
||||||
dateSubmittedCell.innerHTML = dateSubmitted;
|
dateSubmittedCell.innerHTML = dateSubmitted;
|
||||||
dateSubmittedCell.classList.add("d-none", "d-md-table-cell"); // Column visible on medium and larger displays
|
dateSubmittedCell.classList.add("d-none", "d-md-table-cell"); // Column visible on medium and larger displays
|
||||||
bodyRow.insertCell(4).appendChild(actionButton);
|
bodyRow.insertCell(3).appendChild(actionButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
table.style.visibility = "visible";
|
table.style.visibility = "visible";
|
||||||
|
@ -468,7 +554,6 @@ if (newReportForm) {
|
||||||
console.log("Payload:\n" + payload);
|
console.log("Payload:\n" + payload);
|
||||||
const type = reportType.NEW;
|
const type = reportType.NEW;
|
||||||
makeAjaxRequest("POST", url, createReportForm, type, payload);
|
makeAjaxRequest("POST", url, createReportForm, type, payload);
|
||||||
this.reset();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -494,7 +579,7 @@ document.addEventListener("submit", function(event) {
|
||||||
saveButton.appendChild(document.createTextNode(" Saving..."));
|
saveButton.appendChild(document.createTextNode(" Saving..."));
|
||||||
const formData = new FormData(event.target);
|
const formData = new FormData(event.target);
|
||||||
const url = getEndpointDomain() + "api/v1/report/" + event.target.dataset.rid + "/section/" + event.target.dataset.sid;
|
const url = getEndpointDomain() + "api/v1/report/" + event.target.dataset.rid + "/section/" + event.target.dataset.sid;
|
||||||
makeAjaxRequest("PUT", url, saveSectionCallback, saveButton, formData);
|
makeAjaxRequest("PUT", url, updateSection, saveButton, formData);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
|
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
|
||||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous">
|
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous">
|
||||||
|
<link rel="stylesheet" href="css/style.css">
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
|
||||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script>
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/classlist/1.2.20171210/classList.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/classlist/1.2.20171210/classList.min.js"></script>
|
||||||
|
|
Loading…
Reference in a new issue