[!DOCTYPE html]
[html lang="en"]
[head]
[title]Details Form[/title]
[link rel="stylesheet" href="style.css"]
[script src="https://code.jquery.com/jquery-3.6.0.min.js"][/script]
[link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/18.2.1/css/intlTelInput.css"]
[script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/18.2.1/js/intlTelInput.min.js"][/script]
[/head]
[body]
[h2]Details Form[/h2]
[br][br]
[form id="mainForm"]
[div class="row"]
[div class="field"]
[label]Client Company Name[/label]
[input type="text" id="company" name="company"]
[div id="companyErr" class="error"][/div]
[/div]
[div class="field"]
[label]Contact Email[/label]
[input type="text" id="email" name="email"]
[div id="emailErr" class="error"][/div]
[/div]
[/div]
[div class="row"]
[div class="field"]
[label]Username[/label]
[input type="text" id="username" name="username"]
[div id="usernameErr" class="error"][/div]
[/div]
[div class="field"]
[label]Domain Name(s)[/label]
[div class="inline"]
[input type="text" id="domain" placeholder="example.com"]
[button type="button" id="addDomain"]+[/button] [/div]
[div id="domainErr" class="error"][/div]
[div id="domainList" style="margin-top: 10px;"][/div]
[/div]
[/div]
[div class="row"]
[div class="field"]
[label]Contact Phone[/label]
[input type="tel" id="phone" name="phone"]
[div id="phoneErr" class="error"][/div]
[/div]
[div class="field"]
[label]Gender[/label]
[div class="genrow"]
[input type="radio" name="gender" value="Male" id="m1"]
[label for="m1" class="g1"]Male[/label]
[/div]
[div class="genrow"]
[input type="radio" name="gender" value="Female" id="f1"]
[label for="f1" class="g1"]Female[/label]
[/div]
[div id="genderErr" class="error"][/div]
[/div]
[/div]
[div class="row"]
[div class="field"]
[label]Contract Start Date[/label]
[input type="date" id="startDate" name="startDate" class="date"]
[div id="dateStartErr" class="error"][/div]
[/div]
[div class="field"]
[label]Contract End Date[/label]
[input type="date" id="endDate" name="endDate" class="date"]
[div id="dateEndErr" class="error"][/div]
[/div]
[/div]
[div class="row"]
[div class="field"]
[label]Which services are you interested in?[/label]
[div class="services-grid"]
[label][input type="checkbox" name="service" class="service" value="Software development"] Software development[/label]
[label][input type="checkbox" name="service" class="service" value="Graphic design"] Graphic design[/label]
[label][input type="checkbox" name="service" class="service" value="Content writing"] Content writing[/label]
[label][input type="checkbox" name="service" class="service" value="Marketing & advertising"] Marketing & advertising[/label]
[label][input type="checkbox" name="service" class="service" value="Data entry"] Data entry[/label]
[label][input type="checkbox" name="service" class="service" value="Customer support"] Customer support[/label]
[label][input type="checkbox" name="service" class="service" value="Event management"] Event management[/label]
[label][input type="checkbox" name="service" class="service" value="Accounting"] Accounting[/label]
[label][input type="checkbox" name="service" class="service" value="Web Development"] Web Development[/label]
[label][input type="checkbox" name="service" class="service" value="Legal Services"] Legal Services[/label]
[/div]
[/div]
[/div]
[div class="rowServiceErr"]
[div id="serviceErr" class="error"][/div]
[/div]
[div class="rowSave"]
[button type="button" id="saveBtn"]SAVE[/button]
[div id="submitErr" class="error"][/div]
[/div]
[/form]
[div id="modal"]
[div id="modalContent"]
[h3]Confirm Details[/h3]
[div id="modalData"][/div]
[label id="modalSuccess"][/label]
[div class="modalRow"]
[button id="confirmSubmit" class="modalButton"]Submit[/button]
[button id="cancelSubmit" class="modalButton"]Cancel[/button]
[/div]
[/div]
[/div]
[script src="index.js"][/script]
[/body]
[/html]
js
$(document).ready(function () {
/* ==========================================================================
SECTION 1: CONSTANTS & CONFIGURATION
========================================================================== /
const CONFIG = {
REGEX: {
DOMAIN: /^(?!://)([a-zA-Z0-9-_]+.)[a-zA-Z0-9][a-zA-Z0-9-]+.[a-zA-Z]{2,11}?$/,
TEXT: /^[A-Za-z ]+$/,
EMAIL: /^[a-zA-Z0-9.%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/,
USERNAME: /^[A-Za-z0-9]+$/
},
FIELDS: [
{ id: "#company", type: "text", regex: "TEXT", errorId: "#companyErr", msg: "Company name alphabets only" },
{ id: "#email", type: "text", regex: "EMAIL", errorId: "#emailErr", msg: "Enter valid email" },
{ id: "#username", type: "text", regex: "USERNAME", errorId: "#usernameErr", msg: "Username can only have letters and numbers" },
{ id: "input[name='gender']", type: "radio", errorId: "#genderErr", msg: "Select One" },
{ id: ".service", type: "checkbox", errorId: "#serviceErr", msg: "Select at least one Service" }
],
MAX_DOMAINS: 5,
// Mapping form names to readable table labels
LABELS: {
company: "Company Name",
email: "Email Address",
username: "Username",
phone: "Contact Phone",
gender: "Gender",
startDate: "Contract Start",
endDate: "Contract End",
service: "Services",
domains: "Domains" // Added manually
}
};
/* ==========================================================================
SECTION 2: STATE MANAGEMENT
========================================================================== */
const state = {
domains: [],
iti: null
};
/* ==========================================================================
SECTION 3: DOM ELEMENTS CACHE
========================================================================== */
const $ui = {
form: $("#mainForm"),
phone: $("#phone"),
domain: $("#domain"),
startDate: $("#startDate"),
endDate: $("#endDate"),
addDomainBtn: $("#addDomain"),
saveBtn: $("#saveBtn"),
domainList: $("#domainList"),
modal: $("#modal"),
modalData: $("#modalData"),
modalSuccess: $("#modalSuccess"),
confirmSubmit: $("#confirmSubmit"),
cancelSubmit: $("#cancelSubmit")
};
/* ==========================================================================
SECTION 4: INITIALIZATION
========================================================================== */
function init() {
state.iti = window.intlTelInput(document.querySelector("#phone"), {
initialCountry: 'IN',
separateDialCode: true,
strictMode: true,
utilsScript: "https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/18.2.1/js/utils.js",
countrySearch: true
});
bindEvents();
}
/* ==========================================================================
SECTION 5: EVENT HANDLERS
========================================================================== */
function bindEvents() {
// Validation Events
CONFIG.FIELDS.forEach(field => {
if (field.type === 'text') $(field.id).blur(() => validateField(field));
});
$ui.phone.blur(validatePhone);
$ui.startDate.blur(validateDates);
$ui.endDate.blur(validateDates);
// Domain Events
$ui.addDomainBtn.click(handleAddDomain);
$ui.domain.on('keydown', e => { if (e.key === 'Enter') $ui.addDomainBtn.click(); });
$ui.domain.on('input', () => {
$("#domainErr").text("");
$ui.addDomainBtn.removeClass("input-error");
});
$(document).on("click", ".removeBtn", handleRemoveDomain);
// Form Events
$ui.saveBtn.click(handleSave);
$ui.cancelSubmit.click(() => $ui.modal.hide());
$ui.confirmSubmit.click(handleConfirmSubmit);
}
/* ==========================================================================
SECTION 6: LOGIC & VALIDATION
========================================================================== */
function handleAddDomain() {
const val = $ui.domain.val().trim();
const errEl = $("#domainErr");
if (!CONFIG.REGEX.DOMAIN.test(val)) return showError(errEl, $ui.addDomainBtn, "Invalid Domain");
if (state.domains.includes(val)) return showError(errEl, $ui.addDomainBtn, "Duplicate Domain");
if (state.domains.length >= CONFIG.MAX_DOMAINS) return showError(errEl, $ui.addDomainBtn, "Max Limit Reached");
clearError(errEl, $ui.addDomainBtn);
state.domains.push(val);
rebuildDomainList();
$ui.domain.val("");
}
function handleRemoveDomain() {
const index = $(this).data("index");
state.domains.splice(index, 1);
rebuildDomainList();
}
function rebuildDomainList() {
$ui.domainList.empty();
if (state.domains.length > 0) {
const ul = $("<ul></ul>");
state.domains.forEach((d, i) => {
ul.append(`<li>${d} <button class="removeBtn" data-index="${i}" style="color:red; margin-left:10px;">X</button></li>`);
});
$ui.domainList.append(ul).prepend("<strong>Domains:</strong>");
}
}
function handleSave() {
$(".error").text("");
$(".input-error").removeClass("input-error");
let isValid = true;
CONFIG.FIELDS.forEach(field => { if (!validateField(field)) isValid = false; });
if (!validatePhone()) isValid = false;
if (!validateDates()) isValid = false;
if (state.domains.length === 0) {
$("#domainErr").text("Add at least one domain");
$ui.domain.addClass("input-error");
isValid = false;
}
if (isValid) showModal();
}
/* --------------------------------------------------------------------------
IMPROVED TABLE GENERATION USING FORM DATA
-------------------------------------------------------------------------- */
function showModal() {
// 1. Capture all form data automatically
// Note: Requires inputs to have 'name' attributes
const formData = new FormData(document.getElementById("mainForm"));
// 2. Extract special/complex values manually
// FormData captures the raw phone input, but we want the formatted intl version.
const formattedPhone = state.iti.getNumber();
// FormData captures checkboxes as separate entries, we want a joined string.
const services = formData.getAll("service").join(", ");
// Domains are in our state array, not the form input.
const domainStr = state.domains.map((d, i) => `${i + 1}. ${d}`).join("<br>");
// 3. Prepare Data Object for the Loop
// We start with a base object and overwrite specific keys with our custom logic
const displayData = {
company: formData.get("company"),
email: formData.get("email"),
username: formData.get("username"),
phone: formattedPhone, // Override raw phone
gender: formData.get("gender"),
startDate: formData.get("startDate"),
endDate: formData.get("endDate"),
service: services, // Override raw array
domains: domainStr // Add domains from state
};
// 4. Generate HTML Table Rows dynamically
let tableRows = "";
// We use the CONFIG.LABELS keys to ensure specific order and proper labels
for (const [key, label] of Object.entries(CONFIG.LABELS)) {
const value = displayData[key] || "-"; // Default to dash if empty
tableRows += `
<tr>
<th style="text-align:left; width: 40%;">${label}</th>
<td>${value}</td>
</tr>
`;
}
const tableHtml = `<table>${tableRows}</table>`;
$ui.modalData.html(tableHtml);
$ui.modal.show();
}
function handleConfirmSubmit() {
$ui.modalSuccess.text("Success!");
setTimeout(() => {
$ui.form[0].reset(); // Native form reset
state.domains = [];
rebuildDomainList();
state.iti.setNumber("");
$ui.modal.hide();
$ui.modalSuccess.text("");
$(".input-error").removeClass("input-error");
}, 1500);
}
// --- Validation Helpers ---
function validateField(field) {
const $el = $(field.id);
const $err = $(field.errorId);
if (field.type === "text") {
const val = $el.val().trim();
if (!val) return showError($err, $el, "Required");
if (!CONFIG.REGEX[field.regex].test(val)) return showError($err, $el, field.msg);
} else if ((field.type === "radio" || field.type === "checkbox") && !$(field.id + ":checked").length) {
return showError($err, $el, field.msg);
}
return clearError($err, $el);
}
function validatePhone() {
if (!state.iti.isValidNumber()) return showError($("#phoneErr"), $ui.phone, "Invalid Number");
return clearError($("#phoneErr"), $ui.phone);
}
function validateDates() {
const s = $ui.startDate.val(), e = $ui.endDate.val();
if (!s) return showError($("#dateStartErr"), $ui.startDate, "Required");
clearError($("#dateStartErr"), $ui.startDate);
if (!e) return showError($("#dateEndErr"), $ui.endDate, "Required");
if (s && e && s > e) return showError($("#dateEndErr"), $ui.endDate, "Invalid Date Range");
return clearError($("#dateEndErr"), $ui.endDate);
}
function showError($err, $el, msg) { $err.text(msg); $el.addClass("input-error"); return false; }
function clearError($err, $el) { $err.text(""); $el.removeClass("input-error"); return true; }
init();
});
css
/* General Body Styling */
body {
font-family: Arial, sans-serif;
width: 900px;
margin: 30px auto;
background: #fff;
}
h2 {
text-align: center;
margin-bottom: 25px;
}
/* --- Layout Classes --- */
.row {
display: flex;
gap: 25px;
margin-bottom: 15px;
}
.genrow {
display: flex;
gap: 25px;
}
.field {
flex: 1;
}
/* --- Form Elements --- */
.field label {
display: block;
font-weight: 600;
margin-bottom: 6px;
}
input[type="text"],
input[type="date"],
input[type="tel"] {
width: 100%;
height: 30px;
padding: 8px;
border: 1px solid #bbb;
border-radius: 5px;
box-sizing: border-box;
/* transition for smooth error toggling */
transition: border-color 0.3s;
}
/* ERROR CLASS - Replaces .css('border', 'red') in JS */
.input-error {
border: 2px solid red !important;
}
.date {
margin-top: 6px;
}
/* --- Domain Section --- */
.inline {
display: flex;
gap: 6px;
}
.inline button {
width: 30px;
height: 30px;
align-items: center;
padding: 1px;
font-size: 18px;
cursor: pointer;
}
/* --- Services Grid --- */
.services-grid {
display: grid;
grid-template-columns: auto auto auto;
gap: 10px 25px;
margin-top: 8px;
}
.services-grid label {
font-weight: normal;
}
/* --- Error Text --- /
.error {
color: red;
font-size: 12px;
margin-top: 4px;
min-height: 15px; / Prevents layout shift */
}
#serviceErr {
margin-top: 0;
}
/* --- Table Styling --- */
table {
width: 100%;
margin-top: 8px;
border-collapse: collapse;
}
table th,
table td {
padding: 8px;
border: 1px solid #ccc;
}
table th {
background: #f5f5f5;
}
/* --- Buttons --- */
button {
padding: 8px 10px;
cursor: pointer;
}
#saveBtn {
margin: 30px;
width: 80px;
height: 40px;
background-color: rgb(4, 84, 255);
color: white;
border: none;
border-radius: 4px;
}
#addDomain {
width: 30px;
height: 30px;
}
/* --- Modal Styling --- /
#modal {
display: none; / Hidden by default /
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5); / Dimmed background */
z-index: 1000;
}
#modalContent {
background: #fff;
width: 50%;
margin: 80px auto;
padding: 20px;
border-radius: 4px;
position: relative;
}
#modalSuccess {
color: green;
display: block;
text-align: center;
margin-top: 10px;
font-weight: bold;
}
.modalButton {
padding: 10px 20px;
background-color: #ddd;
border: none;
border-radius: 4px;
}
#confirmSubmit {
background-color: #4CAF50;
color: white;
}
#cancelSubmit {
background-color: #f44336;
color: white;
}
.modalRow {
display: flex;
justify-content: center;
gap: 20px;
margin-top: 20px;
}
.rowSave {
display: flex;
justify-content: flex-end;
}
.rowServiceErr {
text-align: center;
}