diff --git a/backend/core/migrations/0014_alter_organization_customer_base.py b/backend/core/migrations/0014_alter_organization_customer_base.py new file mode 100644 index 0000000..3ea7bfb --- /dev/null +++ b/backend/core/migrations/0014_alter_organization_customer_base.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.3 on 2025-05-06 16:56 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0013_alter_organization_change_rate_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='organization', + name='customer_base', + field=models.CharField(blank=True, help_text='How would you characterize your customer base distribution?', max_length=255, null=True), + ), + ] diff --git a/backend/core/models.py b/backend/core/models.py index 0a38687..1d7d566 100644 --- a/backend/core/models.py +++ b/backend/core/models.py @@ -57,7 +57,7 @@ class Organization(models.Model): third_party_vendor_access = models.CharField(max_length=20, help_text="How many third-party vendors have access to your systems?") internal_software_development = models.CharField(max_length=20, help_text="What is the extent of your internal software development activities?") geographic_scope = models.CharField(max_length=20, null=True, blank=True, help_text="What is your organization's geographic operational scope?") - customer_base = models.CharField(max_length=20, null=True, blank=True, help_text="How would you characterize your customer base distribution?") + customer_base = models.CharField(max_length=255, null=True, blank=True, help_text="How would you characterize your customer base distribution?") customer_type = models.CharField(max_length=20, null=True, blank=True, help_text="What is your primary customer type?") product_portfolio = models.CharField(max_length=20, null=True, blank=True, help_text="How diversified is your product/service portfolio?") supplier_base = models.CharField(max_length=20, null=True, blank=True, help_text="What is your supplier base structure?") diff --git a/backend/core/static/js/formHandling.js b/backend/core/static/js/formHandling.js index 05d8c47..856fd2a 100644 --- a/backend/core/static/js/formHandling.js +++ b/backend/core/static/js/formHandling.js @@ -73,13 +73,13 @@ function setUpNavigation() { // 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('change',()=> setNextButtonAvailability()); + input.addEventListener('blur', () => setNextButtonAvailability()); + }); - - } -function setNextButtonAvailability() { +async function setNextButtonAvailability() { console.log('Setting next button availability'); // check if current question is answered // and then enable the next button, disable it otherwise @@ -91,8 +91,14 @@ function setNextButtonAvailability() { 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 = true; + nextEnabled = await validateFormFields(nextButton, errorDiv); + }else{ + nextButton.disabled = true; + if (errorDiv) errorDiv.textContent = ''; + return } } else { const inputs = currentQuestion.querySelectorAll('input, select, textarea'); @@ -181,4 +187,23 @@ function setButtonVisiblity(buttonId, visible) { } } +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; + } +} + diff --git a/backend/core/templates/signup.html b/backend/core/templates/signup.html index 5a5b334..aa21b36 100644 --- a/backend/core/templates/signup.html +++ b/backend/core/templates/signup.html @@ -11,7 +11,16 @@
{% if form.errors %}
- {{ form.errors }} +
{% endif %} {% csrf_token %} @@ -27,6 +36,7 @@ Enter the organization name and your email address. Both fields are required to continue. +
diff --git a/backend/core/urls.py b/backend/core/urls.py index 9df5a33..07d1232 100644 --- a/backend/core/urls.py +++ b/backend/core/urls.py @@ -13,4 +13,5 @@ urlpatterns = [ path('preview//', v.template_preview, name='template_preview'), path("payment/", v.payment_page, name="payment_page"), path('pdf//', v.pdf_view, name='pdf_view'), + path('api/validate_form_fields/', v.validate_form_fields, name='validate_form_fields'), ] diff --git a/backend/core/views.py b/backend/core/views.py index 1f2245d..f28b080 100644 --- a/backend/core/views.py +++ b/backend/core/views.py @@ -3,14 +3,17 @@ import yaml from django.shortcuts import render, redirect , get_object_or_404 from .forms import OrganizationForm -from .models import Organization,Document, DocumentTemplate,DocumentRiskControl,Risk +from .models import Organization,Document, DocumentTemplate from backend.accounts.utils import send_confirmation_email, send_document_email from django.contrib.admin.views.decorators import staff_member_required -from .utils import generate_pdf, map_weight_to_impact_likelihood, calculate_aggregate_weight, calculate_aggregate_likelihood, generate_risk_graph +from .utils import generate_pdf, generate_risk_graph from .tables import risk_matrix_table ,get_risk_table from django.conf import settings site_domain = settings.SITE_DOMAIN from .processors import render_template +from django.http import JsonResponse +from django.core.exceptions import ValidationError +from django.core.validators import validate_email @@ -45,6 +48,25 @@ def signup(request): return render(request, 'signup.html', {'form': form}) +def validate_form_fields(request): + name = request.GET.get('name', '').strip() + email = request.GET.get('email', '').strip() + errors = {} + + if name and Organization.objects.filter(name__iexact=name).exists(): + errors['name'] = 'Organization with this name already exists.' + if email: + try: + validate_email(email) + except ValidationError: + errors['email'] = 'Please enter a valid email address.' + else: + if Organization.objects.filter(email__iexact=email).exists(): + errors['email'] = 'This email is already registered.' + + return JsonResponse({'errors': errors}) + + def thankyou(request): return render(request, 'thankyou.html')