Build a Customer Complaint Form - Build a Customer Complaint Form

Tell us what’s happening:

My code is working as intended and requested, but still getting failed tasks:

Failed: 24. When all of the checkboxes from #complaints-group are changed to the unchecked state, you should set #complaints-group’s border color to red.
Failed: 30. Your isValid function should take the validateForm() as its argument and return true when all the form fields have been filled correctly.
Failed: 32. You should call isValid when you try to submit the form.

Your code so far

<!-- file: index.html -->
<!DOCTYPE html>
<html lang="en">

<head>
    <title>Customer Complaint Form</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="styles.css">
</head>

<body>
    <h1>Complaint Form</h1>
    <form id="form">
        <fieldset id="personal-info">
            <div>
                <label for="full-name">Full Name:</label>
                <input type="text" id="full-name" name="full-name" placeholder="John Doe">
            </div>

            <div>
                <label for="email">Email Address:</label>
                <input type="email" id="email" name="email" placeholder="example@domain.com">
            </div>
        </fieldset>
        <hr>
        <fieldset id="product-info">
            <div>
                <label for="order-no">Order No:</label>
                <input type="text" id="order-no" name="order-no" placeholder="2024######">
            </div>
            <div>
                <label for="product-code">Product Code:</label>
                <input type="text" id="product-code" name="product-code" placeholder="XX##-X###-XX#">
            </div>
            <div>
                <label for="quantity">Quantity:</label>
                <input type="number" id="quantity" name="quantity" min="1">
            </div>
        </fieldset>

        <fieldset id="complaints-group">
            <legend>Complaint Reason:</legend>
            <div>
                <input type="checkbox" id="damaged-product" name="complaint" value="damaged-product">
                <label for="damaged-product">Damaged Product</label>
            </div>

            <div>
                <input type="checkbox" id="nonconforming-product" name="complaint" value="nonconforming-product">
                <label for="nonconforming-product">Nonconforming Product</label>
            </div>

            <div>
                <input type="checkbox" id="delayed-dispatch" name="complaint" value="delayed-dispatch">
                <label for="delayed-dispatch">Delayed Dispatch</label>
            </div>

            <div>
                <input type="checkbox" id="other-complaint" name="complaint" value="other">
                <label for="other-complaint">Other</label>
            </div>
        </fieldset>

        <div id="complaint-description-container">
            <legend>Description of Complaint Reason</legend>
            <textarea placeholder="Describe the reason of your complaint in at least 20 characters"
                name="complaint-textarea" id="complaint-description"></textarea>
        </div>

        <fieldset id="solutions-group">
            <legend>Desired Solution</legend>
            <input type="radio" name="solutions" id="refund" value="refund">
            <label for="refund">Refund</label>

            <input type="radio" name="solutions" id="exchange" value="exchange">
            <label for="exchange">Exchange</label>

            <input type="radio" name="solutions" id="other-solution" value="other">
            <label for="other-solution">Other</label>
        </fieldset>

        <div id="solution-description-container">
            <legend>Description of Desired Solution</legend>
            <textarea placeholder="Describe the desired solution to your issue in at least 20 characters"
                name="solution-textarea" id="solution-description"></textarea>
        </div>
        <div id="btn-container">
            <button type="submit" id="submit-btn">Submit</button>
            <span id="message-box"></span>
        </div>

    </form>

    <script src="script.js"></script>
</body>

</html>
/* file: styles.css */
* {
    box-sizing: border-box;
}

h1 {
    text-align: center;
}

#form {
    max-width: 70%;
    margin: auto;
    border-radius: 10px;
    box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px;
    padding: 10px;
}

input {
    border-color: rgb(118, 118, 118);
}

#personal-info input, #product-info input {
    width: 100%;
    margin-bottom: 10px;
}


fieldset {
    margin-bottom: 10px;
    border-radius: 5px;
    border-color: rgb(118, 118, 118);
}

textarea {
    width: 100%;
    border-color: rgb(118, 118, 118);
}

#btn-container {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

#submit-btn, #clear-btn {
    margin: 10px 15px 0;
}
/* file: script.js */
// Variables
const form = document.getElementById("form")
const fullName = document.getElementById("full-name");
const emailAddress = document.getElementById("email");
const orderNumber = document.getElementById("order-no");
const productCode = document.getElementById("product-code");
const productQuantity = document.getElementById("quantity");
const complaintReason = document.querySelectorAll("input[type=checkbox]")
const complaintDescription = document.getElementById("complaint-description");
const desiredSolution = document.querySelectorAll("input[type=radio]");
const solutionDescription = document.getElementById("solution-description");
const submitButton = document.getElementById("submit-btn");
const messageBox = document.getElementById("message-box");

// Validate Functions
function validateFullName(fullName) {
    return fullName.value != "";
}

function validateEmail(emailAddress) {
    const emailRegex = /^\S+\@\S+\.\S+$/i;
    return emailRegex.test(emailAddress.value)
}

function validateOrderNumber(orderNumber) {
    const orderNumRegex = /^2024\d{6}$/;
    return orderNumRegex.test(orderNumber.value);
}

function validateProductCode(productCode) {
    const productCodeRegex = /^[a-zA-Z]{2}\d{2}\-[a-zA-Z]{1}\d{3}\-[a-zA-Z]{2}\d{1}$/;
    return productCodeRegex.test(productCode.value)
}

function validateQuantity(quantity) {
    const quantityRegex = /^[1-9]+$/;
    return quantityRegex.test(quantity.value)
}

function validateComplaintsGroup(checkedReasons) {
    return checkedReasons.length !== 0;
}

function validateComplaintDescription(checkedReasons) {
    const length = complaintDescription.value.length;
    if (checkedReasons.includes("other")) {
        return length >= 20;
    }
    return length === 0;
}

function validateDesiredSolution(desiredSolution) {
    return desiredSolution.length == 1
}

function validateSolutionDescription(checkedSolutions) {
    if (checkedSolutions.includes("other")) {
        return solutionDescription.value.length >= 20;
    }
    return solutionDescription.value.length == 0;
}

// Get Value Functions
function getCheckedReason() {
    return Array.from(complaintReason)
        .filter(i => i.checked)
        .map(i => i.value);
}

function getDesiredSolution() {
    return Array.from(desiredSolution)
        .filter(i => i.checked)
        .map(i => i.value);
}

// Form
function validateForm() {
    let filledForm = {
        'full-name': validateFullName(fullName),
        'email': validateEmail(emailAddress),
        'order-no': validateOrderNumber(orderNumber),
        'product-code': validateProductCode(productCode),
        'quantity': validateQuantity(productQuantity),
        'complaints-group': validateComplaintsGroup(getCheckedReason()),
        'complaint-description': validateComplaintDescription(getCheckedReason()),
        'solutions-group': validateDesiredSolution(getDesiredSolution()),
        'solution-description': validateSolutionDescription(getDesiredSolution())
    }
    return filledForm
}

function isValid(validatedForm) {
    const arrayForm = Object.values(validatedForm)
    return arrayForm.every((val) => val == true)
}

// Change event
form.addEventListener("change", (event) => {
    var borderColor = validateForm()[event.target.id]? "green" : "red";
    event.target.style.borderColor = borderColor
    if(event.target.type == "checkbox" || event.target.type == "radio"){
        borderColor = validateForm()["complaints-group"]? "green" : "red"
        event.target.closest("fieldset").style.borderColor = borderColor
    }
})

// Helper function to set border color based on validation
function setBorderColor(field, isValid) {
    const borderColor = isValid ? "green" : "red";

    if (NodeList.prototype.isPrototypeOf(field)) {
        field.forEach((element) => {
            element.closest("fieldset").style.borderColor = borderColor;
        });
    } else {
        field.style.borderColor = borderColor;
    }
}


// SUBMIT BUTTON
submitButton.addEventListener("click", (e) => {
    e.preventDefault();
    const form = validateForm();

    if(!isValid(form)){
        setBorderColor(fullName, form['full-name']);
        setBorderColor(emailAddress, form['email']);
        setBorderColor(orderNumber, form['order-no']);
        setBorderColor(productCode, form['product-code']);
        setBorderColor(productQuantity, form['quantity']);

        setBorderColor(complaintReason, form['complaints-group']);
        setBorderColor(complaintDescription, form['complaint-description']);
        setBorderColor(desiredSolution, form['solutions-group']);
        setBorderColor(solutionDescription, form['solution-description']);
    }
});

Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:138.0) Gecko/20100101 Firefox/138.0

Challenge Information:

Build a Customer Complaint Form - Build a Customer Complaint Form

You’ll need to gather some information about what the tests are doing.

For example, I’ve added some console.logs here:


function validateComplaintsGroup(checkedReasons) {

  console.log(checkedReasons.length)
    return checkedReasons.length !== 0;
}


// Change event
form.addEventListener("change", (event) => {
  
    var borderColor = validateForm()[event.target.id]? "green" : "red";
    event.target.style.borderColor = borderColor
    if(event.target.type == "checkbox" || event.target.type == "radio"){
      console.log(event.target.id)
      console.log(event.target.closest("fieldset").id)
        borderColor = validateForm()["complaints-group"]? "green" : "red"
        console.log(borderColor)
        event.target.closest("fieldset").style.borderColor = borderColor
    }
})

Here is my own test by clicking:

1
damaged-product
complaints-group
1
green
0
damaged-product
complaints-group
0
red

Normal, you can see I click damaged-product , checkedReasons.length is 1 and the border color is green.

Then I click it again, length is 0 and border is red. Seems good!

Now I run the tests:

0
0
0
1   //The length has changed to 1
0   //but no feedback from the form event listener
1
damaged-product
complaints-group
1
green    // this is normal
0     //why did it change to 0 ?
nonconforming-product
complaints-group
0
red
1
1
1

Tests are doing something unexpected. You need to continue that investigation. I think maybe form change event listener might not work well in this case.

Interesting. Thank you. Ill look into it. :slight_smile:

1 Like

I think the tests manipulate the checked status directly without triggering a change event on the form?

Still, the length matches the color even in the tests:

1
green
0
red

maybe a console.log for the status of every checkbox in that group ?

Ive been trying to reproduce this error you sent and I just can’t. Could you tell me how can I reproduce it here locally, please?

Sure, here is your code with the console.log that I’ve added:

/* file: script.js */
// Variables
const form = document.getElementById("form")
const fullName = document.getElementById("full-name");
const emailAddress = document.getElementById("email");
const orderNumber = document.getElementById("order-no");
const productCode = document.getElementById("product-code");
const productQuantity = document.getElementById("quantity");
const complaintReason = document.querySelectorAll("input[type=checkbox]")
const complaintDescription = document.getElementById("complaint-description");
const desiredSolution = document.querySelectorAll("input[type=radio]");
const solutionDescription = document.getElementById("solution-description");
const submitButton = document.getElementById("submit-btn");
const messageBox = document.getElementById("message-box");

// Validate Functions
function validateFullName(fullName) {
    return fullName.value != "";
}

function validateEmail(emailAddress) {
    const emailRegex = /^\S+\@\S+\.\S+$/i;
    return emailRegex.test(emailAddress.value)
}

function validateOrderNumber(orderNumber) {
    const orderNumRegex = /^2024\d{6}$/;
    return orderNumRegex.test(orderNumber.value);
}

function validateProductCode(productCode) {
    const productCodeRegex = /^[a-zA-Z]{2}\d{2}\-[a-zA-Z]{1}\d{3}\-[a-zA-Z]{2}\d{1}$/;
    return productCodeRegex.test(productCode.value)
}

function validateQuantity(quantity) {
    const quantityRegex = /^[1-9]+$/;
    return quantityRegex.test(quantity.value)
}

function validateComplaintsGroup(checkedReasons) {

  console.log(checkedReasons.length)
    return checkedReasons.length !== 0;
}

function validateComplaintDescription(checkedReasons) {
    const length = complaintDescription.value.length;
    if (checkedReasons.includes("other")) {
        return length >= 20;
    }
    return length === 0;
}

function validateDesiredSolution(desiredSolution) {
    return desiredSolution.length == 1
}

function validateSolutionDescription(checkedSolutions) {
    if (checkedSolutions.includes("other")) {
        return solutionDescription.value.length >= 20;
    }
    return solutionDescription.value.length == 0;
}

// Get Value Functions
function getCheckedReason() {
    return Array.from(complaintReason)
        .filter(i => i.checked)
        .map(i => i.value);
}

function getDesiredSolution() {
    return Array.from(desiredSolution)
        .filter(i => i.checked)
        .map(i => i.value);
}

// Form
function validateForm() {
    let filledForm = {
        'full-name': validateFullName(fullName),
        'email': validateEmail(emailAddress),
        'order-no': validateOrderNumber(orderNumber),
        'product-code': validateProductCode(productCode),
        'quantity': validateQuantity(productQuantity),
        'complaints-group': validateComplaintsGroup(getCheckedReason()),
        'complaint-description': validateComplaintDescription(getCheckedReason()),
        'solutions-group': validateDesiredSolution(getDesiredSolution()),
        'solution-description': validateSolutionDescription(getDesiredSolution())
    }
    return filledForm
}

function isValid(validatedForm) {
    const arrayForm = Object.values(validatedForm)
    return arrayForm.every((val) => val == true)
}

// Change event
form.addEventListener("change", (event) => {
  console.log("form change")
    var borderColor = validateForm()[event.target.id]? "green" : "red";
    event.target.style.borderColor = borderColor
    if(event.target.type == "checkbox" || event.target.type == "radio"){
      console.log(event.target.id)
      console.log(event.target.closest("fieldset").id)
        borderColor = validateForm()["complaints-group"]? "green" : "red"
        
        event.target.closest("fieldset").style.borderColor = borderColor
    }
    console.log(event.target.closest("fieldset").id,event.target.closest("fieldset").style.borderColor)
})

// Helper function to set border color based on validation
function setBorderColor(field, isValid) {
    const borderColor = isValid ? "green" : "red";

    if (NodeList.prototype.isPrototypeOf(field)) {
        field.forEach((element) => {
          console.log(field)
            element.closest("fieldset").style.borderColor = borderColor;
        });
    } else {
      console.log(field)
        field.style.borderColor = borderColor;
    }
}


// SUBMIT BUTTON
submitButton.addEventListener("click", (e) => {
    e.preventDefault();
    const form = validateForm();

    if(!isValid(form)){
        setBorderColor(fullName, form['full-name']);
        setBorderColor(emailAddress, form['email']);
        setBorderColor(orderNumber, form['order-no']);
        setBorderColor(productCode, form['product-code']);
        setBorderColor(productQuantity, form['quantity']);

        setBorderColor(complaintReason, form['complaints-group']);
        setBorderColor(complaintDescription, form['complaint-description']);
        setBorderColor(desiredSolution, form['solutions-group']);
        setBorderColor(solutionDescription, form['solution-description']);
    }
});

Just need to click “Run the Tests”

Thanks. But I meant the test you run on your side that is causing the error.
I did write a bunch of console.logs on my side to see if I can figure it out.

Hm, not sure what you mean? I’m not able to replicate the error at all by clicking manually on the checkboxes, it appears to function perfectly as intended.

The fCC automated tests that run are doing or checking something unexpected, although I’m not sure what it is yet.

I’ve got something…

The Text for tests 16 and 24 is the same.

  1. When all of the checkboxes from #complaints-group are changed to the unchecked state, you should set #complaints-group’s border color to red.
  1. When all of the checkboxes from #complaints-group are changed to the unchecked state, you should set #complaints-group’s border color to red.

Test 24 is actually checking the solutions group, not the complaints group.

I actually opened this issue, but it was only closed yesterday and hasn’t been pushed live yet: https://github.com/freeCodeCamp/freeCodeCamp/issues/60104

So you need to troubleshoot the solutions group, not the complaints group. This is tricky because you cannot manually uncheck radio buttons, but the tests do it anyways.

Oh that’s interesting. :sweat_smile:
I did not check whether any was unchecked because I figured you can’t.
I’ll see if fixing that solves the problem.

Ok, so I did this change and it solved 24! Yeeey

removed by mod

Now is only 30 and 32…

1 Like

Nice work! Please don’t post solution code to the forum though

30 is a bit vague but 32 there is an important keyword here “submit the form”, which is different from just clicking a button.

Oh sorry. Forgot. Wont post solution again.

Ok. So I was so focused on the previous failed task that I forgot to rewrite the submit.
I fixed that and it solved task 32.
I’ve been working on my code and writing and rewriting it and I just can’t figure task 30 out.
I thought maybe it was expecting ‘true’ for the descriptions if ‘other’ is not checked independently of the characters written? But that wouldn’t make much sense.
Any other tips for me, please? :grimacing:

Ok. So that was it.
I think this is wrong. If I don’t select ‘other’, the validation for the description should be false if anything is written on it. It just doesn’t make any sense, right?
I added a bunch of console.logs and saw that some tasks performed were selecting something other than ‘other’ and writing something on the description (16 characters), being it for the complaint or the solution.
So, not only it should have at least 20 characters, but the fact the ‘other’ was not selected, than it should have zero characters.
But that’s just what I think it makes sense. It would mess up the database from this hypothetical website, because they would have ‘description’ when ‘other’ was not selected.

Hi, where are you at with this? Are you still on test 30?

Please post your updated code if you still need assistance.

No. I solved it. Though, as stated above, I do think the solution is wrong. If you could and is fitting, would you check on that?
Thank you a lot for the assistance.

I get your reasoning but, on the other hand, why wouldn’t it be acceptable for a user to describe the damage to a product or how it did not conform? That keeps the form flexible.

That’s true, but it also makes the database a mess. I guess this hypothetical website would have to decide why it is collecting this data in the first place to than decide how to proceed. :smiley: