Welcome, Admin!
Enter a Form ID to load and submit:
Error: Form definition is invalid.
"; console.error("Invalid form fields definition:", formDefinition.fields); return; } formDefinition.fields.forEach((field) => { const div = document.createElement("div"); const label = document.createElement("label"); label.htmlFor = `field-${field.name}`; label.textContent = field.label || field.name; // Use label, fallback to name div.appendChild(label); let input; // Basic type handling - could be expanded switch (field.type) { case "textarea": // Allow explicit textarea type case "string": // Use textarea for string if maxLength suggests it might be long if (field.maxLength && field.maxLength > 100) { input = document.createElement("textarea"); input.rows = 4; // Default rows } else { input = document.createElement("input"); input.type = "text"; } if (field.minLength) input.minLength = field.minLength; if (field.maxLength) input.maxLength = field.maxLength; break; case "email": input = document.createElement("input"); input.type = "email"; break; case "url": input = document.createElement("input"); input.type = "url"; break; case "number": input = document.createElement("input"); input.type = "number"; if (field.min !== undefined) input.min = field.min; if (field.max !== undefined) input.max = field.max; input.step = field.step || "any"; // Allow decimals by default break; case "boolean": input = document.createElement("input"); input.type = "checkbox"; // Checkbox label handling is slightly different label.insertBefore(input, label.firstChild); // Put checkbox before text input.style.width = "auto"; // Override default width input.style.marginRight = "10px"; break; // Add cases for 'select', 'radio', 'date' etc. if needed default: input = document.createElement("input"); input.type = "text"; console.warn( `Unsupported field type "${field.type}" for field "${field.name}". Rendering as text.` ); } if (input.type !== "checkbox") { // Checkbox is already appended inside label div.appendChild(input); } input.id = `field-${field.name}`; input.name = field.name; // Crucial for form data collection if (field.required) input.required = true; if (field.placeholder) input.placeholder = field.placeholder; if (field.pattern) input.pattern = field.pattern; // Add regex pattern validation publicForm.appendChild(div); }); const submitButton = document.createElement("button"); submitButton.type = "submit"; submitButton.textContent = "Submit Form"; publicForm.appendChild(submitButton); } publicForm.addEventListener("submit", async (e) => { e.preventDefault(); showStatus(""); const formId = e.target.dataset.formId; if (!formId) { showStatus("Error: Form ID is missing.", true); return; } const formData = new FormData(e.target); const submissionData = {}; // Convert FormData to a plain object, handling checkboxes correctly for (const [key, value] of formData.entries()) { const inputElement = e.target.elements[key]; // Handle Checkboxes (boolean) if (inputElement && inputElement.type === "checkbox") { // A checkbox value is only present in FormData if it's checked. // We need to ensure we always send a boolean. // Check if the element exists in the form (it might be unchecked) submissionData[key] = inputElement.checked; } // Handle Number inputs (convert from string) else if (inputElement && inputElement.type === "number") { // Only convert if the value is not empty, otherwise send null or handle as needed if (value !== "") { submissionData[key] = parseFloat(value); // Or parseInt if only integers allowed if (isNaN(submissionData[key])) { // Handle potential parsing errors if input validation fails console.warn(`Could not parse number for field ${key}: ${value}`); submissionData[key] = null; // Or keep as string, or show error } } else { submissionData[key] = null; // Or undefined, depending on backend expectation for empty numbers } } // Handle potential multiple values for the same name (e.g., multi-select), though not rendered here else if (submissionData.hasOwnProperty(key)) { if (!Array.isArray(submissionData[key])) { submissionData[key] = [submissionData[key]]; } submissionData[key].push(value); } // Default: treat as string else { submissionData[key] = value; } } // Ensure boolean fields that were *unchecked* are explicitly set to false // FormData only includes checked checkboxes. Find all checkbox inputs in the form. const checkboxes = e.target.querySelectorAll('input[type="checkbox"]'); checkboxes.forEach((cb) => { if (!submissionData.hasOwnProperty(cb.name)) { submissionData[cb.name] = false; // Set unchecked boxes to false } }); console.log("Submitting data:", submissionData); // Debugging try { // Public submission endpoint doesn't require auth const result = await makeApiRequest( `/forms/${formId}/submissions`, "POST", submissionData, false ); showStatus( `Submission successful! Submission ID: ${result.submission_id}` ); e.target.reset(); // Clear the form // Optionally hide the form after successful submission // publicFormArea.classList.add('hidden'); } catch (error) { let errorMsg = `Submission failed: ${error.message}`; // Handle validation errors specifically if (error.validationErrors) { errorMsg = "Submission failed due to validation errors:\n"; for (const [field, message] of Object.entries(error.validationErrors)) { errorMsg += `- ${field}: ${message}\n`; } // Highlight invalid fields? (More complex UI update) } showStatus(errorMsg, true); } }); // --- Initial Setup --- toggleSections(); // Set initial view based on stored token if (authToken) { loadFormsButton.click(); // Auto-load forms if logged in } });