Files
old-riskletpy/backend/core/static/js/formHandling.js
2025-09-17 15:24:34 +02:00

282 lines
11 KiB
JavaScript

document.addEventListener('DOMContentLoaded', (event) => {
const form = document.querySelector('form');
const formElements = form.elements;
setUpNavigation()
});
function nextQuestion() {
document.currentQuestion++;
hideNavElementsAndQuestions();
showQuestion(`q${document.currentQuestion}`);
setButtonVisiblity('back', true);
setButtonVisiblity('next', true);
if (document.currentQuestion === document.lastQuestion) {
setButtonVisiblity('next', false);
setButtonVisiblity('submit', true);
}
setNextButtonAvailability();
}
function previousQuestion() {
if (document.currentQuestion > 0) {
document.currentQuestion--;
hideNavElementsAndQuestions();
showQuestion(`q${document.currentQuestion}`);
setButtonVisiblity('next', true);
setButtonVisiblity('submit', false);
document.nextEnabled = true;
}
setButtonVisiblity('back', document.currentQuestion !== 0);
setNextButtonAvailability();
}
function setUpNavigation() {
const questions = document.querySelectorAll('.question');
document.currentQuestion = 0;
document.nextEnabled = false;
document.lastQuestion = questions.length - 1;
hideNavElementsAndQuestions();
showQuestion(`q${document.currentQuestion}`);
setButtonVisiblity('next', true);
const nextButton = document.getElementById('next');
const backButton = document.getElementById('back');
nextButton.addEventListener('click', nextQuestion);
backButton.addEventListener('click', previousQuestion);
setNextButtonAvailability();
// check if next button should be enabled on every input, checkbox and radio button bellow class of .question change
const inputs = document.querySelectorAll('.question input, .question select, .question textarea');
inputs.forEach(input => {
input.addEventListener('change',()=> setNextButtonAvailability());
input.addEventListener('blur', () => setNextButtonAvailability());
});
}
async function setNextButtonAvailability() {
// check if current question is answered
// and then enable the next button, disable it otherwise
const currentQuestion = document.getElementById(`q${document.currentQuestion}`);
const nextButton = document.getElementById('next');
const submitButton = document.getElementById('submit');
// check if any input in the current question is checked, or filled in case it is a text input
let nextEnabled = false;
if (document.currentQuestion === 0) {
const name = document.getElementById('name');
const email = document.getElementById('email');
const errorDiv = document.getElementById('org-email-error');
if (name && email && name.value.trim() && email.value.trim()) {
nextEnabled = await validateFormFields(nextButton, errorDiv);
}else{
nextButton.disabled = true;
if (errorDiv) errorDiv.textContent = '';
return
}
} else {
const inputs = currentQuestion.querySelectorAll('input, select, textarea');
for (let input of inputs) {
if (input.checkVisibility() === false){
continue;
}
if (input.type === 'checkbox' || input.type === 'radio') {
if (input.checked) {
nextEnabled = true;
break;
}
} else {
if (input.value) {
nextEnabled = true;
break;
}
}
}
}
nextButton.disabled = !nextEnabled;
submitButton.disabled = !nextEnabled;
}
function saveFormState(elements) {
const formState = {};
for (let element of elements) {
if (element.name) {
if (element.type === 'select-multiple') {
formState[element.name] = Array.from(element.selectedOptions).map(option => option.value);
} else if (element.type === 'checkbox' || element.type === 'radio') {
formState[element.name] = element.checked ? element.value : formState[element.name] || null;
} else {
formState[element.name] = element.value;
}
}
}
localStorage.setItem('formState', JSON.stringify(formState));
}
function loadFormState(elements) {
const formState = JSON.parse(localStorage.getItem('formState'));
if (formState) {
for (let element of elements) {
if (element.name && formState[element.name] !== undefined) {
if (element.type === 'select-multiple') {
Array.from(element.options).forEach(option => {
option.selected = formState[element.name].includes(option.value);
});
} else if (element.type === 'checkbox' || element.type === 'radio') {
element.checked = formState[element.name] === element.value;
} else {
element.value = formState[element.name];
}
}
}
}
}
function hideNavElementsAndQuestions() {
const questions = document.querySelectorAll('.question');
questions.forEach(question => {
// add bootstrap hidden class to the element
question.classList.add('d-none');
});
const nextButton = document.getElementById('next');
const backButton = document.getElementById('back');
const submitButton = document.getElementById('submit');
nextButton.classList.add('d-none');
backButton.classList.add('d-none');
submitButton.classList.add('d-none');
}
function showQuestion(questionId) {
const question = document.getElementById(questionId);
question.classList.remove('d-none');
progressBar();
if (questionId == 'q7') {
setupSensitiveDataValidator();
}
}
function setButtonVisiblity(buttonId, visible) {
const button = document.getElementById(buttonId);
if (visible) {
button.classList.remove('d-none');
} else {
button.classList.add('d-none');
}
}
async function validateFormFields(nextBtn, errorDiv) {
const nameInput = document.getElementById('name');
const emailInput = document.getElementById('email');
const name = nameInput.value.trim();
const email = emailInput.value.trim();
const resp = await fetch(`/api/validate_form_fields/?name=${name}&email=${email}`);
const data = await resp.json();
if (data.errors && (data.errors.name || data.errors.email)) {
errorDiv.textContent = (data.errors.name || '') + ' ' + (data.errors.email || '');
nextBtn.disabled = true;
return false;
} else {
errorDiv.textContent = '';
nextBtn.disabled = false;
return true;
}
}
function progressBar() {
const basic = Array.from(document.querySelectorAll('.basic-section'));
const advanced = Array.from(document.querySelectorAll('.advanced-section'));
const basicProgress = document.getElementById('basic-progress');
const advancedProgress = document.getElementById('advanced-progress');
const basicBar = document.getElementById('basic-progress-bar');
const advancedBar = document.getElementById('advanced-progress-bar');
const basicBarWrap = document.getElementById('basic-progress-bar-wrap');
const advancedBarWrap = document.getElementById('advanced-progress-bar-wrap');
let currentId = `q${document.currentQuestion}`;
let idxBasic = basic.findIndex(q => q.id === currentId);
let idxAdvanced = advanced.findIndex(q => q.id === currentId);
if (idxBasic !== -1) {
basicProgress.classList.remove('d-none');
basicBarWrap.classList.remove('d-none');
advancedProgress.classList.add('d-none');
advancedBarWrap.classList.add('d-none');
if (idxBasic === basic.length) {
basicProgress.innerHTML = `<span class="text-success fw-bold">Basic question &#10003;</span>`;
basicBarWrap.classList.add('d-none');
} else {
basicProgress.innerText = `Question ${idxBasic + 1} / ${basic.length}`;
}
let percent = ((idxBasic + 1) / basic.length) * 100;
basicBar.style.width = percent + "%";
basicBar.setAttribute('aria-valuenow', percent);
} else if (idxAdvanced !== -1) {
advancedProgress.classList.remove('d-none');
advancedBarWrap.classList.remove('d-none');
basicBarWrap.classList.add('d-none');
advancedProgress.innerText = `Advanced Question ${idxAdvanced + 1} / ${advanced.length}`;
let percent = ((idxAdvanced + 1) / advanced.length) * 100;
advancedBar.style.width = percent + "%";
advancedBar.setAttribute('aria-valuenow', percent);
} else {
basicProgress.classList.add('d-none');
advancedProgress.classList.add('d-none');
basicBarWrap.classList.add('d-none');
advancedBarWrap.classList.add('d-none');
}
}
function setupSensitiveDataValidator() {
const dataTypes = [
{checkbox: 'personal_applicable', radios: 'personal_impact'},
{checkbox: 'financial_applicable', radios: 'financial_impact'},
{checkbox: 'ip_applicable', radios: 'ip_impact'},
{checkbox: 'operational_applicable', radios: 'operational_impact'},
{checkbox: 'government_applicable', radios: 'government_impact'}
];
function updateRadios(type) {
const cb = document.getElementById(type.checkbox);
const radios = document.querySelectorAll(`input[name="${type.radios}"]`);
const noneCb = document.getElementById('none_applicable');
if (noneCb && noneCb.checked) {
radios.forEach(radio => {
radio.disabled = true;
radio.checked = false;
});
return;
}
radios.forEach(radio => {
radio.disabled = !cb.checked;
if (!cb.checked) radio.checked = false;
});
}
dataTypes.forEach(type => {
const cb = document.getElementById(type.checkbox);
if (cb) {
cb.addEventListener('change', () => updateRadios(type));
updateRadios(type);
}
});
const noneCb = document.getElementById('none_applicable');
if (noneCb) {
noneCb.addEventListener('change', function() {
if (noneCb.checked) {
['personal_applicable','financial_applicable','ip_applicable','operational_applicable','government_applicable'].forEach(id => {
const cb = document.getElementById(id);
if (cb) cb.checked = false;
});
}
dataTypes.forEach(type => updateRadios(type));
});
}
}