satyacode
Background
Back to js-datatype-intermediate

11-jugaad-form-validator.js

JavaScript
1/**
2 * 📋 Jugaad Form Validator - Indian Style!
3 *
4 * India mein form bharna ek art hai! College admission ka form validate
5 * karna hai. Har field ke apne rules hain. Tujhe ek errors object return
6 * karna hai jisme galat fields ke error messages hain. Agar sab sahi hai
7 * toh empty errors object aur isValid = true.
8 *
9 * formData object:
10 *   { name, email, phone, age, pincode, state, agreeTerms }
11 *
12 * Validation Rules:
13 *   1. name: must be a non-empty trimmed string, min 2 chars, max 50 chars
14 *      Error: "Name must be 2-50 characters"
15 *
16 *   2. email: must be a string containing exactly one "@" and at least one "."
17 *      after the "@". Use indexOf(), lastIndexOf(), includes().
18 *      Error: "Invalid email format"
19 *
20 *   3. phone: must be a string of exactly 10 digits, starting with 6, 7, 8, or 9
21 *      (Indian mobile numbers). Check each char is a digit.
22 *      Error: "Invalid Indian phone number"
23 *
24 *   4. age: must be a number between 16 and 100 inclusive, and an integer.
25 *      JUGAAD: Agar string mein number diya hai (e.g., "22"), toh parseInt()
26 *      se convert karo. Agar convert nahi ho paya (isNaN), toh error.
27 *      Error: "Age must be an integer between 16 and 100"
28 *
29 *   5. pincode: must be a string of exactly 6 digits, NOT starting with "0"
30 *      Error: "Invalid Indian pincode"
31 *
32 *   6. state: Use optional chaining (?.) and nullish coalescing (??) -
33 *      if state is null/undefined, treat as "". Must be a non-empty string.
34 *      Error: "State is required"
35 *
36 *   7. agreeTerms: must be truthy (Boolean(agreeTerms) === true).
37 *      Falsy values: 0, "", null, undefined, NaN, false
38 *      Error: "Must agree to terms"
39 *
40 * Return:
41 *   { isValid: boolean, errors: { fieldName: "error message", ... } }
42 *   - isValid is true ONLY when errors object has zero keys
43 *
44 * Hint: Use typeof, Boolean(), parseInt(), isNaN(), Number.isInteger(),
45 *   ?. (optional chaining), ?? (nullish coalescing), Object.keys(),
46 *   startsWith(), trim(), length
47 *
48 * @param {object} formData - Form fields to validate
49 * @returns {{ isValid: boolean, errors: object }}
50 *
51 * @example
52 *   validateForm({
53 *     name: "Rahul Sharma", email: "rahul@gmail.com", phone: "9876543210",
54 *     age: 20, pincode: "400001", state: "Maharashtra", agreeTerms: true
55 *   })
56 *   // => { isValid: true, errors: {} }
57 *
58 *   validateForm({
59 *     name: "", email: "bad-email", phone: "12345", age: 10,
60 *     pincode: "0123", state: null, agreeTerms: false
61 *   })
62 *   // => { isValid: false, errors: { name: "...", email: "...", ... } }
63 */
64export function validateForm(formData) {
65
66  if (!formData || typeof formData !== "object") {
67    return {
68      isValid: false,
69      errors: { form: "Invalid form data" }
70    };
71  }
72
73  const errors = {};
74
75  const name = formData?.name;
76
77  if (
78    typeof name !== "string" ||
79    name.trim().length < 2 ||
80    name.trim().length > 50
81  ) {
82    errors.name = "Name must be 2-50 characters";
83  }
84
85
86  const email = formData?.email;
87
88  if (typeof email !== "string") {
89    errors.email = "Invalid email format";
90  } else {
91    const atIndex = email.indexOf("@");
92    const lastAtIndex = email.lastIndexOf("@");
93
94    if (
95      atIndex === -1 ||
96      atIndex !== lastAtIndex || 
97      !email.slice(atIndex).includes(".")
98    ) {
99      errors.email = "Invalid email format";
100    }
101  }
102
103
104  const phone = formData?.phone;
105
106  if (
107    typeof phone !== "string" ||
108    phone.length !== 10 ||
109    !["6", "7", "8", "9"].includes(phone[0])
110  ) {
111    errors.phone = "Invalid Indian phone number";
112  } else {
113    for (let char of phone) {
114      if (char < "0" || char > "9") {
115        errors.phone = "Invalid Indian phone number";
116        break;
117      }
118    }
119  }
120
121  let age = formData?.age;
122
123  if (typeof age === "string") {
124    age = parseInt(age);
125  }
126
127  if (
128    typeof age !== "number" ||
129    isNaN(age) ||
130    !Number.isInteger(age) ||
131    age < 16 ||
132    age > 100
133  ) {
134    errors.age = "Age must be an integer between 16 and 100";
135  }
136
137
138  const pincode = formData?.pincode;
139
140  if (
141    typeof pincode !== "string" ||
142    pincode.length !== 6 ||
143    pincode.startsWith("0")
144  ) {
145    errors.pincode = "Invalid Indian pincode";
146  } else {
147    for (let char of pincode) {
148      if (char < "0" || char > "9") {
149        errors.pincode = "Invalid Indian pincode";
150        break;
151      }
152    }
153  }
154
155  const state = formData?.state ?? "";
156
157  if (
158    typeof state !== "string" ||
159    state.trim().length === 0
160  ) {
161    errors.state = "State is required";
162  }
163
164  if (!Boolean(formData?.agreeTerms)) {
165    errors.agreeTerms = "Must agree to terms";
166  }
167
168  const isValid = Object.keys(errors).length === 0;
169
170  return {
171    isValid,
172    errors
173  };
174}
175
176