Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,8 @@ infra_options.json
_deploy.sh
HackUCF.ovpn
config.yml
*.pem
.env
tests/.env
.venv/
onboardlite.egg-info/
2 changes: 1 addition & 1 deletion app/forms/2.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
},
{
"label": "NID",
"caption": "This is the UCF identifier with two letters and six numbers. It's in your school email!",
"caption": "This is the UCF identifier with two letters and six numbers. It's in your school email! (Format: ab123456)",
"input": "nid",
"key": "nid",
"required": true
Expand Down
2 changes: 1 addition & 1 deletion app/forms/edit.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
},
{
"label": "NID",
"caption": "This is the UCF identifier with two letters and six numbers. It's in your school email!",
"caption": "This is the UCF identifier with two letters and six numbers. It's in your school email! (Format: ab123456)",
"input": "nid",
"key": "nid",
"required": true
Expand Down
164 changes: 147 additions & 17 deletions app/static/form.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,42 +141,134 @@ function get_body() {
return body;
}

// NID validation and formatting functions
function isValidNID(nid) {
// UCF NID format: 2 lowercase letters followed by 6 digits
const nidPattern = /^([a-z]{2}[0-9]{6})$/;
return nidPattern.test(nid);
}

function formatNIDValue(value) {
if (!value) return value;
// Convert to lowercase and remove any non-alphanumeric characters
return value.toLowerCase().replace(/[^a-z0-9]/g, '');
}

function getSpecificNIDError(formattedValue) {
if (!formattedValue) {
return " (required - format: ab123456)";
}

if (formattedValue.length < 8) {
return ` (too short - need ${8 - formattedValue.length} more characters)`;
}

if (formattedValue.length > 8) {
return " (too long - should be 8 characters)";
}

// Check specific pattern issues
const firstTwoChars = formattedValue.substring(0, 2);
const lastSixChars = formattedValue.substring(2);

if (!/^[a-z]{2}$/.test(firstTwoChars)) {
return " (must start with 2 letters)";
}

if (!/^[0-9]{6}$/.test(lastSixChars)) {
return " (must end with 6 numbers)";
}

return " (invalid format - use ab123456)";
}

function validateNIDField(element, is_loud) {
const value = element.value;
const formattedValue = formatNIDValue(value);

// Auto-format the field value as the user types
if (value !== formattedValue) {
element.value = formattedValue;
}

const isValid = formattedValue && isValidNID(formattedValue);

if (is_loud) {
// Clear all previous error messages
if (element.placeholder) {
element.placeholder = element.placeholder
.replaceAll(" (required!)", "")
.replaceAll(" (invalid format!)", "")
.replaceAll(" (required - format: ab123456)", "")
.replace(/ \(too short - need \d+ more characters?\)/g, "")
.replaceAll(" (too long - should be 8 characters)", "")
.replaceAll(" (must start with 2 letters)", "")
.replaceAll(" (must end with 6 numbers)", "")
.replaceAll(" (invalid format - use ab123456)", "");
}

if (!isValid) {
element.style.background = "var(--hackucf-error)";
element.style.color = "white";
if (element.placeholder) {
element.placeholder += getSpecificNIDError(formattedValue);
}
} else {
// Reset styling for valid input
element.style.background = "var(--hackucf-off-white)";
element.style.color = "black";
}
}

return isValid;
}

function validate_required(is_loud) {
const els = document.querySelectorAll("[required]");
let result = true;

for (let i = 0; i < els.length; i++) {
let value = get_value(els[i]);
let element = els[i];

// Special handling for NID fields
if (element.nodeName === "INPUT" && element.name === "nid") {
if (!validateNIDField(element, is_loud)) {
result = false;
}
continue;
}

let value = get_value(element);
// Undefined checks
if (typeof value == "undefined" || !RegExp(els[i].pattern).test(value)) {
if (typeof value == "undefined" || !RegExp(element.pattern).test(value)) {
// is_loud makes us populate 'validation required' texts.
if (is_loud) {
if (els[i].nodeName == "FIELDSET") {
els[i].style.color = "var(--hackucf-error)";
els[i].style.fontWeight = "bold";
if (element.nodeName == "FIELDSET") {
element.style.color = "var(--hackucf-error)";
element.style.fontWeight = "bold";
} else {
els[i].style.background = "var(--hackucf-error)";
els[i].style.color = "white";
if (els[i].placeholder) {
els[i].placeholder = els[i].placeholder.replaceAll(
element.style.background = "var(--hackucf-error)";
element.style.color = "white";
if (element.placeholder) {
element.placeholder = element.placeholder.replaceAll(
" (required!)",
"",
);
els[i].placeholder += " (required!)";
element.placeholder += " (required!)";
}
}
}
result = false;
} else if (is_loud) {
// Revert previous style changes if input filled out.
if (els[i].nodeName == "FIELDSET") {
els[i].style.color = "var(--text)";
els[i].style.fontWeight = "normal";
if (element.nodeName == "FIELDSET") {
element.style.color = "var(--text)";
element.style.fontWeight = "normal";
} else {
els[i].style.background = "var(--hackucf-off-white)";
els[i].style.color = "black";
if (els[i].placeholder) {
els[i].placeholder = els[i].placeholder.replaceAll(
element.style.background = "var(--hackucf-off-white)";
element.style.color = "black";
if (element.placeholder) {
element.placeholder = element.placeholder.replaceAll(
" (required!)",
"",
);
Expand Down Expand Up @@ -336,6 +428,44 @@ window.onload = (evt) => {
document.getElementById("apple_wallet").style.display = "block";
}

// Add real-time validation for NID fields
const nidInputs = document.querySelectorAll('input[name="nid"]');
nidInputs.forEach(function(nidInput) {
nidInput.addEventListener('input', function(event) {
const formatted = formatNIDValue(event.target.value);
if (event.target.value !== formatted) {
event.target.value = formatted;
}

// Real-time validation feedback with specific error messages
if (formatted.length > 0) {
if (isValidNID(formatted)) {
event.target.style.background = "var(--hackucf-off-white)";
event.target.style.color = "black";
// Clear any error messages from placeholder
if (event.target.placeholder) {
event.target.placeholder = event.target.placeholder
.replaceAll(" (required - format: ab123456)", "")
.replace(/ \(too short - need \d+ more characters?\)/g, "")
.replaceAll(" (too long - should be 8 characters)", "")
.replaceAll(" (must start with 2 letters)", "")
.replaceAll(" (must end with 6 numbers)", "")
.replaceAll(" (invalid format - use ab123456)", "");
}
} else {
// Show gentle feedback while typing, more specific on blur
event.target.style.background = "#ffeeee";
event.target.style.color = "black";
}
}
});

nidInput.addEventListener('blur', function(event) {
// Validate on blur (when user leaves the field) with specific error messages
validateNIDField(event.target, true);
});
});

// Infra checker
fetch("https://horizon.hackucf.org", { mode: "no-cors" })
.then((evt) => {
Expand Down