//This is a hacked in fix for the bug with the multiple select fields in the form //the issue is that on page load, that field gets focussed and the page scrolls to it //this is a workaround to prevent that from happening // Store scroll position immediately let initialScrollTop = 0; let scrollHandled = false; window.addEventListener("DOMContentLoaded", function () { initialScrollTop = window.pageYOffset || document.documentElement.scrollTop; // Immediately set up scroll listener window.addEventListener("scroll", function onScroll() { if (!scrollHandled) { scrollHandled = true; window.scrollTo(0, initialScrollTop); window.removeEventListener("scroll", onScroll); } }, { passive: true }); // Defer field fix just a little setTimeout(function () { const offending = document.querySelector("select[id='crcd2_disabilties_1']"); if (offending) { offending.blur(); const chosenInput = offending.nextElementSibling?.querySelector("input"); if (chosenInput) chosenInput.blur(); // Focus your intended first field const fields = Array.from(document.querySelectorAll("form input:not([type=hidden]):not([disabled]):not([readonly]), form select:not([disabled]):not([readonly])")); const firstField = fields.find(el => el.offsetParent !== null); // check it's visible if (firstField) firstField.focus(); } }, 90); // shorter delay to beat the layout flicker }); //Web API ajax wrapper (function(webapi, $) { function safeAjax(ajaxOptions) { var deferredAjax = $.Deferred(); shell.getTokenDeferred().done(function(token) { // Add headers for ajax if (!ajaxOptions.headers) { $.extend(ajaxOptions, { headers: { "__RequestVerificationToken": token } }); } else { ajaxOptions.headers["__RequestVerificationToken"] = token; } $.ajax(ajaxOptions) .done(function(data, textStatus, jqXHR) { validateLoginSession(data, textStatus, jqXHR, deferredAjax.resolve); }).fail(deferredAjax.reject); //ajax }).fail(function() { deferredAjax.rejectWith(this, arguments); // On token failure pass the token ajax and args }); return deferredAjax.promise(); } webapi.safeAjax = safeAjax; })(window.webapi = window.webapi || {}, jQuery) //START the actual logic for navigation of form steps //and final submission to add the referral to Dynamics document.addEventListener("DOMContentLoaded", function () { let referralData = {}; const referralPaths = { 'whoareyou': '/', 'carer': '/referral-about-the-carer/', 'cared_for': '/referral-about-the-cared-for/', 'gp_practice': '/referral-gp-practice/', 'parent_or_guardian': '/referral-parent-or-guardian/', //only if < 18 'second_parent_or_guardian': '/referral-parent-or-guardian/referral-second-parent-or-guardian/', //only if < 18 and ticked the second parent box 'school': '/referral-school/', //only if <18 'referrer': '/referral-about-referrer/', 'info': '/referral-info/', 'gp_doctor': '/referral-gp-doctor/', }; //=========================== START - GET form step from temporary storage and set values ==========================// async function getReferralFromTempStorage() { const storageId = sessionStorage.getItem("referralStorageId"); console.log('storageId', storageId, window.location.pathname) if (!storageId) { //alert("Session expired. Please restart the referral."); if(window.location.pathname !== '/') { window.location.href = '/'; // Redirect to the first page } return; } referralData = {}; // Step 1: Get full data from temp storage try { const result = await webapi.safeAjax({ type: "GET", url: `/_api/crcd2_referralformtempdatas(crcd2_id='${storageId}')?$select=crcd2_tempdata` }); referralData = JSON.parse(result.crcd2_tempdata || "{}"); } catch (error) { //if no referral data, delete the storage id console.log('Loaded referral data from API:', referralData); if (!referralData || Object.keys(referralData).length === 0) { console.log('No referral data found in temp storage. Deleting storage ID.'); sessionStorage.removeItem("referralStorageId"); return; } } try { // Step 2: Get the current form name from the URL const currentFormName = getCurrentFormName(); console.log('currentFormName', currentFormName); // Step 3: Get the specific form data for the current form const currentFormData = referralData[currentFormName] || {}; console.log('referralData for current form', currentFormData); //Step 4: do all the changes to the form before we're setting the values changeNumberOnlyFields(); //create multiselect fields from the conditions renderGroupedConditionMultiselect(currentFormData); // Step 5: Set the form values const form = document.getElementById("liquid_form"); if (!form) { console.warn("Form not found in the DOM. Cannot set values."); return currentFormData; } const inputs = form.querySelectorAll("input, select, textarea"); inputs.forEach(input => { const fieldName = input.getAttribute("id"); if (fieldName && currentFormData[fieldName] !== undefined) { input.value = currentFormData[fieldName]; } }); // Handle radio buttons const radioGroups = {}; inputs.forEach(input => { if (input.type === "radio") { const groupName = input.name; if (!radioGroups[groupName]) { radioGroups[groupName] = []; } radioGroups[groupName].push(input); } }); // Set the value for each radio group for (const groupName in radioGroups) { const group = radioGroups[groupName]; // Extract cleaned key from full group name const cleanedKey = groupName.split('$').pop(); let checkedValue = currentFormData[cleanedKey]; if (checkedValue === "true") { checkedValue = 1; } else if (checkedValue === "false") { checkedValue = 0; } console.debug('radio group', groupName, cleanedKey, checkedValue); group.forEach(input => { console.debug('Setting radio input', input.value, 'to checked:', input.value == checkedValue); input.checked = (input.value == checkedValue); }); } // Handle checkboxes inputs.forEach(input => { if (input.type === "checkbox") { console.log('checkbox input', input) const fieldName = input.getAttribute("id"); console.log('checkbox fieldName', fieldName, currentFormData[fieldName]) if (fieldName && currentFormData[fieldName] !== undefined) { input.checked = currentFormData[fieldName]; } } }); //Handle birth dates //check if we have to set a default date if(referralData && referralData[currentFormName]?.birthdate) { const dobInput = document.getElementById('birthdate_datepicker_description'); //format the date to the format expected by the input dobInput.value = moment(referralData[currentFormName].birthdate).format("MMMM DD, YYYY"); } // Handle custom toggle (e.g., crcd2_doyouliveinsurrey) const toggleWrapper = document.querySelector("#crcd2_doyouliveinsurrey_Container .custom-toggle-select input[type='checkbox']"); if (toggleWrapper && currentFormData["crcd2_doyouliveinsurrey"]) { toggleWrapper.checked = currentFormData["crcd2_doyouliveinsurrey"] === "684750000"; // yes value } //Step 6: //Now set all the other page changes based on the referral data hideReferrerOrganisation(); setPageTitle(); setToggle(); toggleTextareasWithToggles(); handleCarerAgeFieldVisibility({ dobFieldId: 'birthdate_datepicker_description', fieldsToToggle: [ ] }); } catch (err) { console.error("Failed to load referral data from API"); console.error("Error message:", err?.message || err); console.error("Full error object:", err); alert("There was an error retrieving your referral data. Please restart."); return; } } //if this isn't the thank you page, we need to get the referral data from the temp storage if (!window.location.pathname.includes('/thank-you/')) { getReferralFromTempStorage(); } //=========================== END - GET form step from temporary storage and set values ==========================// //========================== START - If the referrer TYpe is school, don't show org ==========================// function hideReferrerOrganisation() { const refererType = getRefererType(referralData); if(refererType === '684750004') { // School const orgContainer = document.getElementById("crcd2_organisationtemp"); //get closest tr const orgRow = orgContainer?.closest("tr"); if (orgRow) { orgRow.style.display = "none"; } } } //========================== END - If the referrer TYpe is school, don't show org ==========================// //========================== START - Set dynamic page title based on who is filling in the form ==========================// function setPageTitle() { //check if ths is the carers page and the user who fills in the form is a carer const path = window.location.pathname; const refererType = getRefererType(referralData); let titleElement = document.querySelector(".tab-title"); //get disability and conditions title via aria label = "Disabilty or Condition Information" and then get the .section-title and inside that the h3 const disabilityTitle = document.querySelector('fieldset[aria-label="Disabilty or Condition Information"] .section-title h3'); if (titleElement) { /*'684750000': general, // Carer '684750001': general, // Cared For '684750002': general, // Parent '684750004': general, // School '684750005': [ // GP Doctor '684750003': agencyOrOther, // Agency '684750006': agencyOrOther // Other*/ if (path.contains(referralPaths['carer']) && refererType === '684750000') { // Logic specific to carer users on the carer page - self referral titleElement.textContent = "About You, the Carer"; if (disabilityTitle) { disabilityTitle.textContent = "Your Disability or Condition Information"; } //get fieldset with aria label "Carer Details" and then .section-title h3 const carerDetailsTitle = document.querySelector('fieldset[aria-label="Carer Details"] .section-title h3'); //change to 'Your Details' if (carerDetailsTitle) { carerDetailsTitle.textContent = "Your Details"; } //Does the carer or the person they care for live in Surrey? const surreyLabel = document.getElementById("crcd2_doyouliveinsurrey_label"); surreyLabel.textContent = surreyLabel.textContent.replace('Does the carer', 'Do you').replace('they', 'you'); //Also tick this if the person you care for is currently in a Surrey Hospital const surreyDescription = document.querySelector('#crcd2_doyouliveinsurrey_label').closest('tr').querySelector('.description.above'); if (surreyDescription) { //Change from: "Also tick this if the person you care for is currently in a Surrey Hospital" surreyDescription.textContent = surreyDescription.textContent.replace('the carer', 'you').replace('they', 'you'); } } else if (path.contains(referralPaths['carer'])) { // Cares page, but not a carer user if (disabilityTitle) { disabilityTitle.textContent = "Carers Disability or Condition Information"; } } if (path.contains(referralPaths['cared_for']) && refererType === '684750000') { // Logic specific to carer users on the cared for page titleElement.textContent = "About Who You Care For"; } if (path.contains(referralPaths['cared_for']) && refererType === '684750001') { // Logic specific to carer users on the cared_for page titleElement.textContent = "About You, the Cared For Person"; } if (path.contains(referralPaths['gp_doctor']) && refererType === '684750005') { // Logic specific to GP users on the GP Doctor page titleElement.textContent = "About You, the GP"; } if (path.contains(referralPaths['parent_or_guardian']) && refererType === '684750002') { // Logic specific to parent or guardian users on the parent or guardian page titleElement.textContent = "About You, the Parent or Guardian of the Carer"; } if (path.contains(referralPaths['second_parent_or_guardian'])) { // Logic specific to parent or guardian users on the second parent or guardian page titleElement.textContent = "Second Parent or Guardian Info"; } if (path.contains(referralPaths['school']) && refererType === '684750004') { // Logic specific to school users on the school page titleElement.textContent = "About You, the School"; } if (path.contains(referralPaths['referrer']) && (refererType === '684750003' || refererType === '684750006' || refererType === '684750004')) { // Logic specific to referrer users on the referrer page titleElement.textContent = "About You, the Referrer"; } } console.log('INFO PAGE - CARER filling in the info page', path, referralPaths['info'], refererType); if(path.contains(referralPaths['info']) && refererType === '684750000') { //carer filling in the info page - SELF REFERRAL // const serviceLabel = document.getElementById("crcd2_servicerequested_label"); serviceLabel.textContent = "Which Services do you require (if known)?"; //remove this option const personalHealthBudgetOption = document.getElementById("crcd2_servicerequested_item4"); if (personalHealthBudgetOption) { const personalHealthBudgetLabel = personalHealthBudgetOption?.closest('label'); personalHealthBudgetLabel.style.display = "none"; } //get all labels and replace 'the Carer' with 'you' and 'Person Cared For' with 'the person you care for' //also take care of 'Does the Carer' and 'Do you' const allLabels = document.querySelectorAll("label.field-label"); allLabels.forEach(label => { label.textContent = label.textContent .replace('Does the carer', 'Do you') .replace('Does the Carer', 'Do you') .replace('Is the Carer', 'Are you') .replace('the Carer', 'you') .replace('the carer', 'you') .replace('they', 'you') .replace('Person Cared For', 'the person you care for'); }); } } //========================== END - Set dynamic page title based on who is filling in the form ==========================// //========================== START - Second parent or guardian page ==========================/ function adjustParentPageForSecondEntry() { const pathToMatch = referralPaths['second_parent_or_guardian']; if (!window.location.pathname.endsWith(pathToMatch)) return; const interval = setInterval(() => { const fieldset = document.querySelector('fieldset[aria-label="Additional Parent or Guardian"]'); const heading = fieldset?.querySelector('h3'); if (fieldset) { fieldset.style.display = 'none'; } if (heading && heading.textContent !== 'Second Parent or Guardian Info') { heading.textContent = 'Second Parent or Guardian Info'; } if (fieldset && heading) clearInterval(interval); }, 300); } adjustParentPageForSecondEntry(); //========================== END - Second parent or guardian page ==========================/ //========================== START - Change surrey tickbox to toggle ==========================// function setToggle() { //all yes/no radios: // Inject CSS for all toggles const radioToggleStyle = document.createElement("style"); radioToggleStyle.textContent = ` .toggle-wrapper, .toggle-label-wrapper { display: flex; align-items: center; gap: 8px; margin-top: 6px; } .toggle-label { font-size: 0.9rem; color: #333; } .custom-toggle-select input[type="checkbox"], .custom-toggle input[type="checkbox"] { opacity: 0; width: 0; height: 0; position: absolute; } .custom-toggle , .custom-toggle-select { position: relative; display: inline-block; width: 50px; height: 24px; } .custom-slider, .toggle-slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; transition: 0.4s; border-radius: 24px; } .custom-slider::before, .toggle-slider::before { position: absolute; content: ""; height: 18px; width: 18px; left: 3px; bottom: 3px; background-color: white; transition: 0.4s; border-radius: 50%; } .custom-toggle input:checked + .custom-slider, .custom-toggle-select input:checked + .toggle-slider { background-color: #2196F3; } .custom-toggle input:checked + .custom-slider::before, .custom-toggle-select input:checked + .toggle-slider::before { transform: translateX(26px); } `; document.head.appendChild(radioToggleStyle); // Convert each .boolean-radio into a toggle document.querySelectorAll(".boolean-radio").forEach(function (radioGroup) { const radios = radioGroup.querySelectorAll('input[type="radio"]'); if (radios.length !== 2) return; // skip if not Yes/No pair const name = radios[0].name; const checkedValue = [...radios].find(r => r.checked)?.value; const isYes = checkedValue === "1" || checkedValue === "true" || checkedValue === 1 || checkedValue === true; console.debug('Converting radio group', name, 'to toggle with checked value:', checkedValue, 'isYes:', isYes); // Remove original radio buttons radioGroup.innerHTML = ""; // Create toggle container const wrapper = document.createElement("div"); wrapper.className = "toggle-wrapper"; const labelNo = document.createElement("span"); labelNo.textContent = "No"; labelNo.className = "toggle-label"; const labelYes = document.createElement("span"); labelYes.textContent = "Yes"; labelYes.className = "toggle-label"; const toggleLabel = document.createElement("label"); toggleLabel.className = "custom-toggle"; const toggleInput = document.createElement("input"); toggleInput.type = "checkbox"; toggleInput.dataset.trueValue = true; toggleInput.dataset.falseValue = false; toggleInput.checked = isYes; const slider = document.createElement("span"); slider.className = "custom-slider"; toggleLabel.appendChild(toggleInput); toggleLabel.appendChild(slider); wrapper.appendChild(labelNo); wrapper.appendChild(toggleLabel); wrapper.appendChild(labelYes); radioGroup.appendChild(wrapper); // Create hidden real radio inputs (preserves original name) const hiddenNo = document.createElement("input"); hiddenNo.type = "radio"; hiddenNo.name = name; hiddenNo.value = false; hiddenNo.style.display = "none"; const hiddenYes = document.createElement("input"); hiddenYes.type = "radio"; hiddenYes.name = name; hiddenYes.value = true; hiddenYes.style.display = "none"; // Sync toggle state with hidden radios toggleInput.addEventListener("change", function () { if (toggleInput.checked) { hiddenYes.checked = true; hiddenYes.dispatchEvent(new Event("change")); } else { hiddenNo.checked = true; hiddenNo.dispatchEvent(new Event("change")); } }); // Set initial checked radio if (isYes) { hiddenYes.checked = true; hiddenYes.dispatchEvent(new Event("change")); } else { hiddenNo.checked = true; hiddenNo.dispatchEvent(new Event("change")); } radioGroup.appendChild(hiddenNo); radioGroup.appendChild(hiddenYes); }); //just the 'do you live in surrey' tickbox const select = document.getElementById("crcd2_doyouliveinsurrey"); if (!select) return; select.style.display = "none"; const container = document.createElement("div"); container.className = "toggle-label-wrapper"; const labelNo = document.createElement("span"); labelNo.textContent = "No"; labelNo.className = "toggle-label toggle-label-left"; const labelYes = document.createElement("span"); labelYes.textContent = "Yes"; labelYes.className = "toggle-label toggle-label-right"; const toggleWrapper = document.createElement("label"); toggleWrapper.className = "custom-toggle-select"; const toggleInput = document.createElement("input"); toggleInput.type = "checkbox"; const slider = document.createElement("span"); slider.className = "toggle-slider"; toggleWrapper.appendChild(toggleInput); toggleWrapper.appendChild(slider); container.appendChild(labelNo); container.appendChild(toggleWrapper); container.appendChild(labelYes); select.parentNode.insertBefore(container, select.nextSibling); toggleInput.checked = select.value !== ""; toggleInput.addEventListener("change", function () { select.value = toggleInput.checked ? "684750000" : ""; select.dispatchEvent(new Event("change")); }); } //========================== END - Change surrey tickbox to toggle ==========================// function changeNumberOnlyFields() { const numberFieldIds = [ "crcd2_nhsnumber", "crcd2_household", ]; numberFieldIds.forEach(id => { const input = document.getElementById(id); if (input) { input.setAttribute("type", "number"); input.setAttribute("min", "0"); // only allow positive numbers input.setAttribute("step", "1"); // only allow whole numbers } }); } //========================== START - change number only fields ==========================// //========================== END - change number only fields ==========================// //========================== START - set multiselect fields ==========================// function replaceAllMSOSWithCheckboxes() { const lists = document.querySelectorAll("ul.msos-selection"); console.debug("Found MSOS lists:", lists.length); lists.forEach((list) => { const containerDiv = list.closest("div.msos-selection-container"); if (!containerDiv) return; const inputCheckboxes = list.querySelectorAll("input[type='checkbox']"); if (!inputCheckboxes.length) return; const firstCheckbox = inputCheckboxes[0]; const idMatch = firstCheckbox.id.match(/^(.+)_item\d+$/); if (!idMatch) return; const baseId = idMatch[1]; const hiddenInput = document.getElementById(baseId + "_0"); if (!hiddenInput) return; const fullContainer = document.getElementById(baseId + "_Container"); if (!fullContainer) return; // Build checkbox wrapper const wrapper = document.createElement("div"); wrapper.id = baseId + "-checkboxes"; wrapper.className = "msos-checkbox-wrapper"; wrapper.style.marginBottom = "1em"; inputCheckboxes.forEach((cb) => { const value = cb.value; console.log("CHECKBOX VALUE:", value); const labelText = cb.getAttribute("aria-label") || cb.parentElement?.title || value; const originalId = cb.id; const label = document.createElement("label"); label.style.display = "block"; label.style.marginBottom = "4px"; const checkbox = document.createElement("input"); checkbox.type = "checkbox"; checkbox.value = value; checkbox.id = originalId; // <-- preserve original ID checkbox.style.marginRight = "5px"; label.appendChild(checkbox); label.appendChild(document.createTextNode(labelText)); wrapper.appendChild(label); }); // Replace full field container with new checkboxes const parent = fullContainer.parentElement; fullContainer.remove(); parent.appendChild(wrapper); }); } setTimeout(replaceAllMSOSWithCheckboxes, 500); //=========================== END - set multiselect fields ==========================// //========================== START - Toggle more info fields ==========================// const hiddenToggleFields = [ { toggleName: "ctl00$ContentContainer$EntityFormControl_354ecd05d592439fb598b3a11e71f4c5$EntityFormControl_354ecd05d592439fb598b3a11e71f4c5_EntityFormView$crcd2_behaviouremotionaldisability", showElementId: "crcd2_behaviouremotionaldisabilityneeds" }, { toggleName: "ctl00$ContentContainer$EntityFormControl_354ecd05d592439fb598b3a11e71f4c5$EntityFormControl_354ecd05d592439fb598b3a11e71f4c5_EntityFormView$crcd2_medialneeds", showElementId: "crcd2_medicalneeds" }, { toggleName: "ctl00$ContentContainer$EntityFormControl_354ecd05d592439fb598b3a11e71f4c5$EntityFormControl_354ecd05d592439fb598b3a11e71f4c5_EntityFormView$crcd2_extrasupport", showElementId: "crcd2_additionalcomments" }, { toggleName: "ctl00$ContentContainer$EntityFormControl_7cda038b6d9c4d03bef00a285e36b9d5$EntityFormControl_7cda038b6d9c4d03bef00a285e36b9d5_EntityFormView$crcd2_isinhospital", showElementId: "crcd2_hospital" }, { toggleName: "ctl00$ContentContainer$EntityFormControl_7cda038b6d9c4d03bef00a285e36b9d5$EntityFormControl_7cda038b6d9c4d03bef00a285e36b9d5_EntityFormView$crcd2_otheragenciesinvolved", showElementId: "crcd2_otheragenciesdetails" } ]; function toggleTextareasWithToggles() { hiddenToggleFields.forEach(({ toggleName, showElementId }) => { const yesRadio = document.querySelector(`input[type="radio"][name="${toggleName}"][value="true"]`); const noRadio = document.querySelector(`input[type="radio"][name="${toggleName}"][value="false"]`); const textareaRow = document.getElementById(showElementId)?.closest("tr"); if (!yesRadio || !noRadio || !textareaRow) return; const updateVisibility = () => { textareaRow.style.display = yesRadio.checked ? "" : "none"; }; // Initial hide/show updateVisibility(); // Event listeners yesRadio.addEventListener("change", updateVisibility); noRadio.addEventListener("change", updateVisibility); }); } function stripHiddenToggleFields(data) { hiddenToggleFields.forEach(({ toggleName, showElementId }) => { const toggle = document.querySelector(`input[name="${toggleName}"]:checked`); const isYes = toggle?.value === "true" || toggle?.value === "1"; if (!isYes && data.hasOwnProperty(showElementId)) { delete data[showElementId]; // remove unwanted field from submission } }); return data; } //============================ END - Toggle more info fields ==========================// //========================== START - Filter Conditions Multiselect ==========================// async function renderGroupedConditionMultiselect(currentFormData) { const targetRow = document.querySelector("#crcd2_disabilitydetail")?.closest("tr"); if (!targetRow) return; // Fetch conditions from Dataverse let result; try { result = await webapi.safeAjax({ type: "GET", url: "/_api/crcd2_conditions?$select=crcd2_conditionid,crcd2_conditionname,crcd2_primarycondition" }); } catch (e) { console.error("Failed to fetch conditions", e); return; } const conditions = result?.value || []; if (!conditions.length) return; // Group by formatted label const groups = {}; conditions.forEach(c => { const value = c.crcd2_primarycondition || 0; const label = c["crcd2_primarycondition@OData.Community.Display.V1.FormattedValue"] || "Other"; if (!groups[value]) groups[value] = { label, items: [] }; groups[value].items.push(c); }); // Insert before the target row const wrapper = document.createElement("tr"); wrapper.innerHTML = `
`; targetRow.parentNode.insertBefore(wrapper, targetRow); // CSS styles const style = document.createElement("style"); style.innerHTML = ` .condition-group { margin-bottom: 2rem; } .condition-group-title { font-weight: 600; margin-bottom: 0.5rem; } .condition-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 0.4rem 1.5rem; } .condition-option { display: flex; align-items: start; gap: 0.4rem; font-size: 0.95rem; line-height: 1.3; } .condition-option input[type="checkbox"] { width: 14px; height: 14px; margin-top: 0.2rem; } `; document.head.appendChild(style); const wrapperDiv = document.getElementById("conditionGroupWrapper"); const checkboxElements = []; // Build UI Object.entries(groups).sort((a, b) => a[1].label.localeCompare(b[1].label) ).forEach(([value, group]) => { const groupDiv = document.createElement("div"); groupDiv.className = "condition-group"; groupDiv.dataset.groupId = value; const groupTitle = document.createElement("div"); groupTitle.className = "condition-group-title"; groupTitle.textContent = group.label; const grid = document.createElement("div"); grid.className = "condition-grid"; const sortedItems = group.items.sort((a, b) => a.crcd2_conditionname.localeCompare(b.crcd2_conditionname) ); sortedItems.forEach(item => { const id = `cond_${item.crcd2_conditionid}`; const option = document.createElement("div"); option.className = "condition-option"; option.dataset.label = item.crcd2_conditionname.toLowerCase(); option.dataset.group = value; const isChecked = currentFormData[id] ? true : false; option.innerHTML = ` `; checkboxElements.push(option); grid.appendChild(option); }); groupDiv.appendChild(groupTitle); groupDiv.appendChild(grid); wrapperDiv.appendChild(groupDiv); }); // Update hidden field const updateHiddenField = () => { const selected = Array.from(document.querySelectorAll(".condition-checkbox:checked")) .map(cb => cb.value); document.getElementById("crcd2_selectedconditions").value = JSON.stringify(selected); }; document.querySelectorAll(".condition-checkbox").forEach(cb => cb.addEventListener("change", updateHiddenField) ); // Search filter const filterInput = document.getElementById("conditionFilter"); filterInput.addEventListener("input", e => { const query = e.target.value.toLowerCase().trim(); // Show/hide individual options const visibleGroups = {}; checkboxElements.forEach(opt => { const match = opt.dataset.label.includes(query); opt.style.display = match ? "" : "none"; if (match) visibleGroups[opt.dataset.group] = true; }); // Show/hide group wrappers document.querySelectorAll(".condition-group").forEach(group => { const groupId = group.dataset.groupId; group.style.display = visibleGroups[groupId] ? "" : "none"; }); }); } //========================== END - Filter Conditions Multiselect ==========================/ //========================== START - Handle hospital field visibility ==========================/ function setupHospitalToggle() { const toggle = document.querySelector('#crcd2_isinhospital input[type="checkbox"]'); const hospitalCell = document.getElementById('crcd2_hospital')?.closest('td'); if (!toggle || !hospitalCell) return; function updateVisibility() { hospitalCell.style.display = toggle.checked ? '' : 'none'; } toggle.addEventListener('change', updateVisibility); updateVisibility(); // run on page load } // Run this after the DOM is ready setupHospitalToggle(); //========================== START - Handle carer age field visibility - Young vs Adult Carers ==========================/ function handleCarerAgeFieldVisibility(options) { console.debug('AGE CHECK handleCarerAgeFieldVisibility', options) // Default options const { dobFieldId = 'birthdate_datepicker_description', fieldsToToggle = [], threshold = 18 } = options; const toggleFields = (age) => { console.debug('AGE CHECK toggleFields', age, fieldsToToggle) fieldsToToggle.forEach(fieldId => { const row = document.getElementById(fieldId)?.closest('tr'); if (!row) return; row.style.display = age < threshold ? '' : 'none'; }); }; const toggleSection = (age) => { console.debug('AGE CHECK toggleSelection - hide the complete Young Carer section?', age, age >= threshold) const fieldset = document.querySelector('fieldset[aria-label="Young Carer Information"]'); if (!fieldset) return; fieldset.style.display = age < threshold ? '' : 'none'; }; const calculateAge = (dateStr) => { const birthDate = new Date(dateStr); if (isNaN(birthDate)) return null; const today = new Date(); let age = today.getFullYear() - birthDate.getFullYear(); const m = today.getMonth() - birthDate.getMonth(); if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) age--; return age; }; const applyLogic = () => { const dobInput = document.getElementById(dobFieldId); if (!dobInput) return; const age = calculateAge(dobInput.value); console.debug('AGE CHECK age', age, dobInput.value); if (age !== null) { toggleFields(age); toggleSection(age); } }; const waitForDOBInput = async () => { console.debug('AGE CHECK waitForDOBInput') let retries = 0; while (retries < 30) { const dobInput = document.getElementById(dobFieldId); console.debug('AGE CHECK dobinput', dobInput) if (dobInput) { applyLogic(); dobInput.addEventListener('input', applyLogic); dobInput.addEventListener('blur', applyLogic); dobInput.addEventListener('change', applyLogic); let lastValue = dobInput.value; setInterval(() => { if (dobInput.value !== lastValue) { lastValue = dobInput.value; applyLogic(); } }, 500); break; } await new Promise(resolve => setTimeout(resolve, 300)); retries++; } if (retries >= 30) console.warn('DOB input not found after waiting'); }; //hide to start with toggleSection(100); waitForDOBInput(); } function disableRecentDatesInCalendarPopup(minAge = 5, defaultBackYears = 20) { const observer = new MutationObserver(() => { const today = new Date(); const cutoff = new Date(today); cutoff.setFullYear(today.getFullYear() - minAge); const cells = document.querySelectorAll('.bootstrap-datetimepicker-widget td.day'); cells.forEach(cell => { const dayStr = cell.getAttribute('data-day'); if (!dayStr) return; const cellDate = new Date(dayStr); if (cellDate > cutoff) { cell.classList.add('disabled'); cell.querySelector('button')?.setAttribute('disabled', 'true'); } }); const yearButton = document.querySelector('.bootstrap-datetimepicker-widget .picker-switch button[title="Select month"]'); const $input = $('input[aria-labelledby="birthdate_label"]'); if (yearButton && $input.length && !$input.data('viewAdjusted')) { const currentVal = $input.val()?.trim(); const enteredDate = new Date(currentVal); const isEmptyOrToday = !currentVal || isNaN(enteredDate) || enteredDate.toDateString() === today.toDateString(); if (isEmptyOrToday) { const jumpDate = new Date(); jumpDate.setFullYear(jumpDate.getFullYear() - defaultBackYears); $input.val(moment(jumpDate).format("MMMM DD, YYYY")); $input.trigger("change"); $input.data('viewAdjusted', true); } } }); observer.observe(document.body, { childList: true, subtree: true }); } // Also prevent manual typing of too-recent dates function enforceMinAgeOnManualInput(minAge = 5) { const cutoff = new Date(); cutoff.setFullYear(cutoff.getFullYear() - minAge); document.addEventListener("blur", function (e) { if (e.target.id === "birthdate_datepicker_description") { const entered = new Date(e.target.value); if (entered > cutoff) { e.target.value = ""; alert("Date must be at least 5 years ago."); } } }, true); } // Call both on DOM ready disableRecentDatesInCalendarPopup(); enforceMinAgeOnManualInput(); //========================== END - Handle carer age field visibility - Young vs Adult Carers ==========================/ //========================== START - Format mandatory fields question ==========================/ function formatMandatoryFieldsQuestion() { //Questions marked with * are required fields. const infoMandatory = Array.from( document.querySelectorAll('.section-title *') ).find(el => el.textContent.includes('Questions marked with * are required fields.') ); if (!infoMandatory) return; infoMandatory.style.fontStyle = "italic"; infoMandatory.style.setProperty('color', '#000', 'important'); infoMandatory.style.setProperty( 'font-size', 'var(--bs-body-font-size)', 'important' ); infoMandatory.textContent = ''; const redStar = document.createElement('span'); redStar.textContent = '*'; redStar.style.color = 'red'; infoMandatory.appendChild( document.createTextNode('Questions marked with ') ); infoMandatory.appendChild(redStar); infoMandatory.appendChild( document.createTextNode(' are required fields.') ); } formatMandatoryFieldsQuestion(); //========================== END - Format mandatory fields question ==========================/ //Just some sanitisation of the data //check if we have all the data we need to submit the form - if no session started, go back to first page const path = window.location.pathname; const isFirstPage = path === '/'; const storageId = sessionStorage.getItem("referralStorageId"); // Redirect if not on first page and missing data if (!isFirstPage && (!storageId)) { console.warn("Missing session or referrer type. Redirecting to first step."); window.location.href = '/'; return; } // Override WebForm_OnSubmit to force submission blocking window.WebForm_OnSubmit = function () { console.warn("WebForm_OnSubmit blocked form submission."); return false; }; function waitForWebApi(callback) { if (typeof window.webapi !== "undefined" && typeof window.webapi.safeAjax === "function") { console.log("webapi is available, executing script..."); callback(); } else { console.log("webapi not available yet, retrying..."); setTimeout(function () { waitForWebApi(callback); }, 500); // Check every 500ms } } function getCurrentFormName() { let form = document.getElementById("liquid_form"); //now find child with id EntityFormPanel and then get its parent id - get that value as the currentForm name let currentFormName = form.querySelector('#EntityFormPanel').parentElement.id; //hack to make sure we store both parent and second parent data because its the same form with the same parent element id if (path.endsWith(referralPaths['second_parent_or_guardian'])) { currentFormName = currentFormName + '-second-parent'; } return currentFormName; } waitForWebApi(function () { let form = document.getElementById("liquid_form"); // Target form by ID let submitButton = document.getElementById("InsertButton"); // Power Pages Submit Button if (form && submitButton) { form.submit = function () { console.log("Form submission intercepted. Preventing submission."); }; form.addEventListener("submit", async function (event) { event.preventDefault(); // Stop default submission event.stopImmediatePropagation(); // 1️⃣ Check if Power Pages validation failed let validationSummary = document.querySelector(".validation-summary.alert-danger"); console.log("Checking for validation errors...", validationSummary.style.display); if (validationSummary && validationSummary.style.display !== "none") { console.warn("Power Pages validation failed. Errors detected."); // alert("Please correct the errors before submitting."); // 2️⃣ Check for invalid fields in the form let invalidFields = form.querySelectorAll("[aria-invalid='true']"); if (invalidFields.length > 0) { console.warn("Power Pages validation failed. Fix errors before submitting."); invalidFields.forEach(field => { console.warn("Invalid field:", field.name || field.id, "Value:", field.value); }); // alert("Please fix the required fields before submitting."); return; // Stop execution if validation failed } return; // Stop submission if validation summary is visible } console.log("No validation errors. Proceeding with API call..."); try { //now find child with id EntityFormPanel and then get its parent id - get that value as the currentForm name const currentFormName = getCurrentFormName(); console.log('currentForm', currentFormName) let formData = getFormDataFromDynamicsForm(currentFormName); console.log('get referral data 2', formData); console.log(formData); //store the data in the temporary table const allTempReferralData = await saveFormStepToTempStorage(currentFormName, formData); //then go to the next step navigateToPage(currentFormName, allTempReferralData); return false; } catch (error) { console.error("Error:", error); alert("Something went wrong with the form submission. Please try again."); } }, true); // Intercept Power Pages-specific Submit Button Click submitButton.addEventListener("click", function (event) { event.preventDefault(); // Stop default behavior event.stopImmediatePropagation(); // Prevent Power Pages overrides console.log('Prevent default click?'); form.dispatchEvent(new Event("submit", { bubbles: true, cancelable: true })); }); } }); //=========================== START - Save form step to temporary storage and go to next step ==========================// function getRefererType(referralData) { console.debug('getRefererType', referralData); if (!referralData || !referralData['EntityFormControl_f188b40bbf5d4afd92023a1f77a97afc']?.['crcd2_referrertype']) { console.log('ARE WE THERE?'); //do we have an element with id crcd2_referrertype_X a radio control, where we can get the value from? X is a number and there are multiple because its a radio control const referrerTypeElement = document.querySelector('input[id^="crcd2_referrertype_"][type="radio"]:checked'); if (referrerTypeElement) { return referrerTypeElement.value; } } return referralData['EntityFormControl_f188b40bbf5d4afd92023a1f77a97afc']?.['crcd2_referrertype']; } async function navigateToPage(currentForm, allTempReferralData) { console.log('navigateToPage', currentForm, allTempReferralData) const general = [ referralPaths['whoareyou'], referralPaths['carer'], referralPaths['cared_for'], referralPaths['info'], referralPaths['gp_practice'], referralPaths['parent_or_guardian'], //only if < 18 referralPaths['second_parent_or_guardian'], //only if <18 and ticked the second parten box referralPaths['school'] //only if <18 AND the carer is in school - crcd2_schoolstatus == 684750000 ]; const agencyOrOther = [...general, referralPaths['referrer']]; const school = [ referralPaths['whoareyou'], referralPaths['carer'], referralPaths['cared_for'], referralPaths['info'], referralPaths['parent_or_guardian'], //only if < 18 referralPaths['second_parent_or_guardian'], //only if <18 and ticked the second parten box referralPaths['school'], referralPaths['referrer'] ] const formSequences = { '684750000': general, // Carer '684750001': general, // Cared For '684750002': general, // Parent '684750004': school, // School '684750005': [ // GP Doctor '/', referralPaths['carer'], referralPaths['cared_for'], referralPaths['info'], referralPaths['gp_practice'], referralPaths['gp_doctor'], referralPaths['school'], //only if <18 AND the carer is in school - crcd2_schoolstatus == 684750000 ], '684750003': agencyOrOther, // Agency '684750006': agencyOrOther // Other }; const path = window.location.pathname; console.log('Whats the referral data', referralData); const refererType = getRefererType(referralData); let sequence = formSequences[refererType]; if (!sequence) { console.warn('No sequence defined for referrer type', refererType); return; } // Remove /referral-parent-or-guardian/ and /referral-school/ if DOB >= 18 AND the carer is in school - crcd2_schoolstatus == 684750000 const birthdateStr = allTempReferralData["EntityFormControl_354ecd05d592439fb598b3a11e71f4c5"]?.['birthdate']; const isInSchool = allTempReferralData["EntityFormControl_354ecd05d592439fb598b3a11e71f4c5"]?.['crcd2_schoolstatus'] === '684750000'; // crcd2_schoolstatus == 684750000 console.debug('schoolStatus - is in school:', isInSchool); if (birthdateStr) { const birthdate = new Date(birthdateStr); const today = new Date(); const age = today.getFullYear() - birthdate.getFullYear(); const monthDiff = today.getMonth() - birthdate.getMonth(); const isBirthdayPassed = monthDiff > 0 || (monthDiff === 0 && today.getDate() >= birthdate.getDate()); const actualAge = isBirthdayPassed ? age : age - 1; console.log('actualAge', actualAge, birthdateStr); // Remove school page if not referred by a school and not in school const refererTypeIsSchool = refererType === '684750004'; sequence = sequence.filter(step => { const isSchoolPage = step === '/referral-school/'; const isParentPage = step === '/referral-parent-or-guardian/' || step === '/referral-parent-or-guardian/referral-second-parent-or-guardian/'; const shouldRemoveSchool = isSchoolPage && !refererTypeIsSchool && !isInSchool; if (actualAge >= 18) { // Remove parent pages and school page if not referred by school or in school return !isParentPage && !shouldRemoveSchool; } else { // For under 18s, remove school page only if required return !shouldRemoveSchool; } }); } // Conditionally remove or keep second parent page if (path === referralPaths['parent_or_guardian']) { const toggle = document.querySelector('#crcd2_secondparent input[type="checkbox"]'); const isSecondParent = toggle?.checked; if (!isSecondParent) { sequence = sequence.filter(step => step !== referralPaths['second_parent_or_guardian']); } } const currentIndex = sequence.indexOf(path); console.log('GOING TO NEXT STEP', currentIndex, sequence, path); if (currentIndex === -1 || currentIndex === sequence.length - 1) { console.log('No next step found or already at the last step.'); // Last step, trigger referral creation createReferralFromTempStorage(); return; } const nextPath = sequence[currentIndex + 1]; window.location.href = nextPath; } async function saveFormStepToTempStorage(currentFormName, formData) { try { // Clean up formData before saving const cleanedFormData = {}; document.querySelectorAll("input[type='checkbox'][id^='crcd2_'][id*='_item']").forEach(cb => { if (cb.checked) { cleanedFormData[cb.id] = cb.value; } }); // Save values from radio groups transformed into toggles document.querySelectorAll(".boolean-radio").forEach(function (radioGroup) { const selectedRadio = radioGroup.querySelector("input[type='radio']:checked"); if (!selectedRadio) return; const cleanedKey = selectedRadio.name.split('$').pop(); if (cleanedKey) { cleanedFormData[cleanedKey] = selectedRadio.value; } }); for (let key in formData) { let val = formData[key]; console.log('-------------CLEAN DATA START key and val', key, val); // Check if the key indicates a radio input (i.e., contains 'ctl00$ContentContainer$') // and the value is "0" or "1" -> convert to boolean true or false if (key.includes('ctl00$ContentContainer$') && (val === "0" || val === "1")) { // Get the cleaned key by removing the prefix (after the last '$') const cleanedKey = key.split('$').pop(); // Convert "0" to false, "1" to true cleanedFormData[cleanedKey] = (val === "0") ? false : true; console.log('-------------CLEAN DATA END radio', key, cleanedKey, cleanedFormData[cleanedKey]); continue; // Skip to the next field after processing the radio input } // Remove the prefix (anything before and including the last dollar sign) const cleanedKey = key.split('$').pop(); // This will get the part after the last dollar sign // Skip all datepicker helper fields if (cleanedKey.endsWith('_datepicker_description')) continue; if ( typeof val === "undefined" || cleanedKey === null || val === null || val === "" || cleanedKey.includes("_selectAll") || cleanedKey.toLowerCase().includes("button") || cleanedKey.match(/^crcd2_.*_item\d+$/) ) { console.log('-------------CLEAN DATA NONE', cleanedKey); continue; } // Convert string "on"/"true"/"false" to actual booleans if (typeof val === "string") { if (val === "on" || val === "true") { cleanedFormData[cleanedKey] = true; continue; } if (val === "off" || val === "false") { cleanedFormData[cleanedKey] = false; continue; } } // Clean up data formats, like birthdate if (typeof val === "string" && /^\d{4}-\d{2}-\d{2}T/.test(val)) { console.log('-------------CLEAN DATA END 0 key and val', cleanedKey, val); cleanedFormData[cleanedKey] = val.split("T")[0]; // Keep only the date part continue; } // Only store checked condition checkboxes // Get only checked condition checkboxes from the DOM const checkedConditions = Array.from( document.querySelectorAll("input[type='checkbox'][id^='cond_']:checked") ).map(input => [input.id, input.value]); // [key, value] if (cleanedKey.startsWith("cond_")) { const isChecked = checkedConditions.some(([id]) => id.endsWith(cleanedKey)); if (!isChecked) { console.log('-------------SKIP UNCHECKED CONDITION', cleanedKey); continue; } } cleanedFormData[cleanedKey] = val; console.log('-------------CLEAN DATA END 2 key and val', cleanedKey, val); } //now get the curren session id from storage: let storageId = sessionStorage.getItem("referralStorageId"); if (!storageId) { const generatedId = crypto.randomUUID(); const payload = { "crcd2_id": generatedId, "crcd2_tempdata": JSON.stringify({ [currentFormName]: cleanedFormData }) }; await webapi.safeAjax({ type: "POST", url: "/_api/crcd2_referralformtempdatas", contentType: "application/json", data: JSON.stringify(payload) }); sessionStorage.setItem("referralStorageId", generatedId); console.log("Created new temp storage with ID:", generatedId); return payload; } else { console.log("Updating existing temp storage record:", storageId); const existing = await webapi.safeAjax({ type: "GET", url: `/_api/crcd2_referralformtempdatas?$filter=crcd2_id eq '${storageId}'&$top=1&$select=crcd2_tempdata` }); const value = existing.value?.[0]; const sysId = value?.crcd2_referralformtempdataid; if (!sysId) { //temp data doesn't exist any longer on the server, so start again, clear session storage and call the function agaain sessionStorage.removeItem("referralStorageId"); console.warn("Temp data not found on server. Starting fresh."); //TODO saveFormStepToTempStorage(currentFormName, formData); return; } let fullData = {}; try { fullData = value?.crcd2_tempdata ? JSON.parse(value.crcd2_tempdata) : {}; } catch { console.warn("Could not parse existing form content, starting fresh."); } fullData[currentFormName] = cleanedFormData; console.log("Full data to be saved:", fullData); await webapi.safeAjax({ type: "PATCH", url: `/_api/crcd2_referralformtempdatas(${sysId})`, contentType: "application/json", data: JSON.stringify({ "crcd2_tempdata": JSON.stringify(fullData) }) }); console.log("Updated temp storage record:", storageId, sysId, fullData); return fullData; } } catch (error) { console.error("Error saving form step to temp storage:", error); alert("There was an error saving your progress. Please try again."); } } //=========================== END - Save form step to temporary storage and go to next step ==========================// async function findPersonByNameOrEmail(data) { const email = data.emailaddress1 || ""; const name = data.firstname || ""; if (!email && !name) return null; const filter = email ? `$filter=emailaddress1 eq '${email}'` : `$filter=firstname eq '${name}'`; const result = await webapi.safeAjax({ type: "GET", url: `/_api/contacts?${filter}&$select=contactid` }); return result.value?.[0]?.contactid || null; } //=========================== START - collect all form data from the forms so we can store it ==========================// function getFormDataFromDynamicsForm() { console.log('getFormDataFromDynamicsForm') let formData = {}; // Select all input, select, and textarea fields inside the form let inputs = document.querySelectorAll("#liquid_form input, #liquid_form select, #liquid_form textarea"); console.log('inputs', inputs) inputs.forEach(input => { let fieldName = input.getAttribute("id"); console.log('fieldName', fieldName, input.type, input.value) if(fieldName === null || fieldName === undefined) { console.warn('Field name is null or undefined, skipping input:', input); return; // Skip if fieldName is not defined } if (input.type === "radio") { //radios don't have an aria-label, so we need to get the name of the radio group fieldName = input.name; if (input.checked) { formData[fieldName] = input.value; } } else if (input.type === "checkbox" && !input.classList.contains('msos-label') && input.checked) { //and does not have class msos-label formData[fieldName] = input.checked; // formData[fieldName] = input.value; } else if (input.type === "hidden" && input.value.startsWith('[{')) { // this is to get fancy multi choice options in a comma separated string // If the hidden input contains a JSON array (multi-select) try { let valueArray = JSON.parse(input.value); // Parse the JSON string let valueString = valueArray.map(item => item.Value).join(','); // Get the Value properties and join them formData[fieldName] = valueString; // Store the comma-separated string in formData } catch (error) { console.error('Failed to parse hidden field value:', input.value, error); } } else if(input.type !== "hidden") { formData[fieldName] = input.value.trim(); // Store input value } console.log('collected field:', fieldName, formData[fieldName]) }); console.log("Collected Form Data:", formData); return formData; } //=========================== END - collect all form data from the forms so we can store it ==========================// //=========================== START - We're done, create the referral ==========================// async function createReferralFromTempStorage() { // Step 1: Get the referral data from the temp table in dynamics via the api const storageId = sessionStorage.getItem("referralStorageId"); if (!storageId) { console.warn("Session expired. Please restart the referral."); if(window.location.pathname !== '/') { window.location.href = '/'; // Redirect to the first page } return; } try { const tempData = await webapi.safeAjax({ type: "GET", url: `/_api/crcd2_referralformtempdatas(crcd2_id='${storageId}')?$select=crcd2_tempdata,crcd2_referralformtempdataid` }); const referralData = cleanReferralForCreation(JSON.parse(tempData.crcd2_tempdata || "{}")); console.debug('referralData for creation', referralData) // Step 2: Submit the referral using the data const gpPracticeId = await createGPPractice(referralData); console.log(gpPracticeId, 'gpPracticeId created') const carerId = await createCarer(referralData, gpPracticeId); console.log(carerId, 'carerId created') const caredForId = await createCaredFor(referralData); console.log(caredForId, 'caredForId created') const parentId = await createParent(referralData, carerId); console.log(parentId, 'parentId created') const schoolId = await createSchool(referralData); console.log(schoolId, 'schoolId created') const gpDoctorId = await createGPDoctor(referralData); console.log(gpDoctorId, 'gpDoctorId created') const otherReferrerId = await createOtherReferrer(referralData); console.log(otherReferrerId, 'otherReferrerId created') // Create the referral with all the collected IDs const referralId = await createReferral(referralData, carerId, caredForId, parentId, schoolId, gpPracticeId, gpDoctorId, otherReferrerId); console.log('referralId', referralId) //now Clean up the temp data if (!referralId) { console.warn("Referral creation failed. Please try again."); alert("There was an error creating your referral. Please try again."); return; } //TEMP TODO UNCOMMENT AGAIN await deleteTempStorage(tempData.crcd2_referralformtempdataid); //no redirect to the thank you page window.location.href = '/thank-you/'; } catch (error) { console.error("Error fetching referral data:", error.message); alert("There was an error fetching your referral data. Please try again."); } } function cleanReferralForCreation(referralData) { let cleanedData = {}; // Loop through each form block for (const formKey in referralData) { const section = referralData[formKey]; const sectionCleaned = { ...section }; const keysToJoin = new Set(); // Step 1: Combine _itemX fields into arrays for (const key in sectionCleaned) { if (key.includes("_item")) { const baseKey = key.split("_item")[0]; if (!Array.isArray(sectionCleaned[baseKey])) { sectionCleaned[baseKey] = []; } sectionCleaned[baseKey].push(sectionCleaned[key]); keysToJoin.add(baseKey); } } // Step 2: Join only combined keys keysToJoin.forEach((key) => { sectionCleaned[key] = sectionCleaned[key].join(","); }); // Step 3: Remove all *_itemX keys Object.keys(sectionCleaned).forEach((key) => { if (key.includes("_item")) { delete sectionCleaned[key]; } }); cleanedData[formKey] = sectionCleaned; //Remove text content for toggle fields that are set to NO/false cleanedData = stripHiddenToggleFields(cleanedData); } console.debug("Cleaned referral data for creation:", cleanedData); return cleanedData; } async function deleteTempStorage(tempDataId) { // Clean up temp data from Dynamics webapi.safeAjax({ type: "DELETE", url: `/_api/crcd2_referralformtempdatas(${tempDataId})`, success: function () { console.log("Temp data deleted successfully."); sessionStorage.removeItem("referralStorageId"); }, error: function (error) { console.warn("Failed to delete temp data:", error); } }); } async function createPerson(referralData, personData, personTypeValue) { console.log('createPerson', referralData, personData, personTypeValue) if (!personData) { console.log(`No data found for formKey ${personTypeValue}. Skipping person creation.`); return null; } // Extract cond_ values const conditionIds = Object.keys(personData) .filter(k => k.startsWith('cond_') && personData[k]) .map(k => k.replace('cond_', '')); if ('conditionFilter' in personData) { delete personData.conditionFilter; } console.debug('conditionIds', conditionIds); const existing = await webapi.safeAjax({ type: "GET", url: `/_api/contacts?$filter=firstname eq '${personData.firstname}' and lastname eq '${personData.lastname}' and emailaddress1 eq '${personData.emailaddress1}'&$top=1&$select=contactid,crcd2_persontype`, contentType: "application/json" }); if (existing.value.length > 0) { const contact = existing.value[0]; const id = contact.contactid; let currentTypes = contact.crcd2_persontype?.split(",").map(v => parseInt(v)) || []; if (!currentTypes.includes(personTypeValue)) { currentTypes.push(personTypeValue); await webapi.safeAjax({ type: "PATCH", url: `/_api/contacts(${id})`, contentType: "application/json", data: JSON.stringify({ crcd2_persontype: currentTypes.join(",") }) }); console.log("Updated existing person with new persontype value"); } if (conditionIds.length) { await linkConditionsToContact(id, conditionIds); } return id; } let cleanedData = { ...personData, crcd2_persontype: `${personTypeValue}` }; //remove the condition ids from the cleanedData object Object.keys(cleanedData).forEach(k => { if (k.startsWith('cond_')) delete cleanedData[k]; }); console.debug('cleanedData for person creation'); console.debug(cleanedData); const newId = await new Promise((resolve, reject) => { webapi.safeAjax({ type: "POST", url: "/_api/contacts", contentType: "application/json", data: JSON.stringify(cleanedData), success: function (data, textStatus, xhr) { const entityId = xhr.getResponseHeader("entityid"); console.log("New person ID:", entityId); resolve(entityId); }, error: function (xhr, status, error) { console.error("Error creating person:", error); reject(error); } }); }); if (conditionIds.length) { await linkConditionsToContact(newId, conditionIds); } return newId; } async function linkConditionsToContact(contactId, conditionIds) { if (!contactId || !conditionIds?.length) return; const baseUrl = window.location.origin; for (const conditionId of conditionIds) { try { await webapi.safeAjax({ type: "POST", url: `/_api/contacts(${contactId})/crcd2_person_condition/$ref`, contentType: "application/json", data: JSON.stringify({ "@odata.id": `${baseUrl}/_api/crcd2_conditions(${conditionId})` }) }); console.log(`Linked condition ${conditionId} to contact ${contactId}`); } catch (err) { console.error(`Error linking condition ${conditionId} to contact ${contactId}`, err); } } } async function createOrganisation(organisationData) { console.log('createOrganisation', organisationData) // Check if organisation exists by name let filter = `name eq '${organisationData.name.replace(/'/g, "''")}'`; const existing = await webapi.safeAjax({ type: "GET", url: `/_api/accounts?$filter=${filter}&$top=1&$select=accountid`, contentType: "application/json" }); if (existing.value.length > 0) { return existing.value[0].accountid; } // Create new organisation const newOrgId = await new Promise((resolve, reject) => { webapi.safeAjax({ type: "POST", url: "/_api/accounts", contentType: "application/json", data: JSON.stringify(organisationData), success: function (data, textStatus, xhr) { const entityId = xhr.getResponseHeader("entityid"); console.log("New organisation created with ID:", entityId); resolve(entityId); }, error: function (xhr, status, error) { console.error("Error creating organisation:", error); reject(error); } }); }); return newOrgId; } async function createConnection(fromContactId, toContactId, role1Id, role2Id) { const payload = { "record1id_contact@odata.bind": `/contacts(${fromContactId})`, "record1roleid@odata.bind": `/connectionroles(${role1Id})`, "record2id_contact@odata.bind": `/contacts(${toContactId})`, "record2roleid@odata.bind": `/connectionroles(${role2Id})` }; try { await webapi.safeAjax({ type: "POST", url: "/_api/connections", contentType: "application/json", data: JSON.stringify(payload) }); console.log(`Created connection from ${fromContactId} to ${toContactId} using role ${role1Id}`); } catch (error) { console.error("Error creating connection:", error); } } async function createCarer(referralData, gpPracticeId) { console.log('createCarer', referralData); const personData = referralData['EntityFormControl_354ecd05d592439fb598b3a11e71f4c5']; const persontypeValue = 684750000; // 'Carer' //if we have a GP Practice Id, connect as GP Practice to the Carer if(gpPracticeId === null || gpPracticeId === undefined) { console.warn("No GP Practice ID provided, skipping connection to GP Practice."); } else { personData["crcd2_GPPractice@odata.bind"] = `/accounts(${gpPracticeId})`; } return await createPerson(referralData, personData, persontypeValue); } async function createCaredFor(referralData) { const personData = referralData['EntityFormControl_5a7b8afdbbf248bcbe560b0f9010e47e']; const persontypeValue = 684750001; // Cared For return await createPerson(referralData, personData, persontypeValue); } async function createParent(referralData, carerId) { const firstParentData = referralData['EntityFormControl_47e9a6105fd54bdd954a27194448a2d3']; //check if the key of the second parent exists in the referralData array console.log('firstParentData', firstParentData); if (firstParentData && firstParentData['crcd2_secondparent'] !== 'false' && referralData['EntityFormControl_47e9a6105fd54bdd954a27194448a2d3-second-parent']) { const secondParentData = referralData['EntityFormControl_47e9a6105fd54bdd954a27194448a2d3-second-parent']; await actuallyCreateParent(secondParentData, carerId); } return await actuallyCreateParent(firstParentData, carerId); } async function actuallyCreateParent(personData, carerId) { const persontypeValue = 684750003; // Other if (!personData) { console.log("No parent form data found, skipping parent creation."); return null; } const parentId = await createPerson(referralData, personData, persontypeValue); const PARENT_ROLE_ID = "EDA69FC6-0B5F-44FB-B584-7DFEB8A925AF"; const CHILD_ROLE_ID = "DF0BF69F-333C-4E9B-86E7-4FF737BC9343"; if(!parentId) { console.warn("Parent ID is not available. Skipping connection creation."); } else { // Parent → Carer await createConnection(parentId, carerId, PARENT_ROLE_ID, CHILD_ROLE_ID); // Carer → Parent (reciprocal) // await createConnection(carerId, parentId, CHILD_ROLE_ID); } return parentId; } async function createSchool(referralData) { // Check if the key exists in referralData if (!referralData['EntityFormControl_ade9444c5dd345eea53e3222227250a9']) { console.warn("School data not found in referralData."); return null; // Return null if the key doesn't exist } //first check if the school already exists //if not, create the school //then return the school id const organisationData = { ...referralData['EntityFormControl_ade9444c5dd345eea53e3222227250a9'], businesstypecode: 1 // School }; const schoolId = await createOrganisation(organisationData); return schoolId; } async function createGPPractice(referralData) { if (!referralData['EntityFormControl_97836fa7f3ca4775a82800f6bff81262']) { console.warn("GP practice data not found in referralData."); return null; // Return null if the key doesn't exist } //first check if the GP practice already exists //if not, create the GP practice //then return the GP practice id console.log('createGPPractice', referralData) const organisationData = { ...referralData['EntityFormControl_97836fa7f3ca4775a82800f6bff81262'], businesstypecode: 684750002 // GP Practice }; console.log('createGPPractice', organisationData) const gpPracticeId = await createOrganisation(organisationData); return gpPracticeId; } async function createGPDoctor(referralData) { //first check if the GP doctor already exists //if not, create the GP doctor //then return the GP doctor id const personData = referralData['EntityFormControl_26623ae5580b4cbe8b24e63087bd2ea0']; // update with actual key const persontypeValue = 684750003; // GP Doctor return await createPerson(referralData, personData, persontypeValue); } async function createOtherReferrer(referralData) { //first check if the other referrer already exists //if not, create the other referrer //then return the other referrer id const personData = referralData['EntityFormControl_a65cf9e8449a4e3989a4af8a4c14dfd3']; // update with actual key const persontypeValue = 684750004; // Other professional //if the Organisation crcd2_organisationtemp has a value, then we need to create an organisation and connect it to the person if(personData?.crcd2_organisationtemp) { const organisationData = { name: personData.crcd2_organisationtemp, businesstypecode: personData?.crcd2_organisationtype || 684750003 // Other }; const organisationId = await createOrganisation(organisationData); if(organisationId) { personData["crcd2_Organisation@odata.bind"] = `/accounts(${organisationId})`; } } return await createPerson(referralData, personData, persontypeValue); } async function createReferral(referralData, carerId, caredForId, parentId, schoolId, gpPracticeId, gpDoctorId, otherReferrerId) { const referralInfoData = referralData['EntityFormControl_7cda038b6d9c4d03bef00a285e36b9d5']; const refererType = getRefererType(referralData); const payload = { "crcd2_CaredForPerson@odata.bind": `/contacts(${caredForId})`, "customerid_contact@odata.bind": `/contacts(${carerId})`, //Carer "crcd2_referralentrymethod": "684750001", //Entry Method - Web Form "crcd2_referralsource": mapReferralSource(referralData), //Referral Source ...referralInfoData }; //connect the GP practice crcd2_GPPractice if we have a GP practice id if (gpPracticeId) { payload["crcd2_GPPractice@odata.bind"] = `/accounts(${gpPracticeId})`; } //connect the school crcd2_school if (schoolId) { payload["crcd2_School@odata.bind"] = `/accounts(${schoolId})`; } // Map referrer to contact or account //set refferal source switch (refererType) { case '684750004': // School payload["crcd2_Referrer@odata.bind"] = `/contacts(${otherReferrerId})`; //set as referring organisation payload["crcd2_ReferrerOrganisation@odata.bind"] = `/accounts(${schoolId})`; //if referrer is school, set 'is school aware' to true crcd2_schoolawareofreferral payload["crcd2_schoolawareofreferral"] = true; payload.crcd2_referralsource = 684750003; //School break; case '684750005': // GP Person payload.crcd2_referralsource = 684750001; //Agency/Professional payload["crcd2_Referrer@odata.bind"] = `/contacts(${gpDoctorId})`; //set GP practice as referring organisation payload["crcd2_ReferrerOrganisation@odata.bind"] = `/accounts(${gpPracticeId})`; break; case '684750003': // Agency case '684750006': // Other payload.crcd2_referralsource = 684750001; //Agency/Professional payload["crcd2_Referrer@odata.bind"] = `/contacts(${otherReferrerId})`; break; case '684750002': // Parent or Guardian payload.crcd2_referralsource = 684750000; //parent or family etc. payload["crcd2_Referrer@odata.bind"] = `/contacts(${parentId})`; break; case '684750000': // Carer payload.crcd2_referralsource = 684750002; // Self payload["crcd2_Referrer@odata.bind"] = `/contacts(${carerId})`; break; case '684750001': // Cared For payload.crcd2_referralsource = 684750000; //parent or family etc. payload["crcd2_Referrer@odata.bind"] = `/contacts(${carerId})`; break; } // Submit referral const referralId = await new Promise((resolve, reject) => { webapi.safeAjax({ type: "POST", url: "/_api/incidents", contentType: "application/json", data: JSON.stringify(payload), success: function (data, textStatus, xhr) { const entityId = xhr.getResponseHeader("entityid"); console.log("Referral created with ID:", entityId); resolve(entityId); }, error: function (xhr, status, error) { console.error("Error creating referral:", error); reject(error); } }); }); return referralId; } function mapReferralSource(referralData) { //set the field 'referralSource' based on the refererType const refererType = getRefererType(referralData); const referrerMapping = { '684750000': '684750002', // Carer -> Self '684750001': '684750005', // Cared For -> Cared for person '684750002': '684750000', // Parent/Guardian -> Parent/Guardian, other Family Member '684750004': '684750003', // School -> School '684750005': '684750006', // GP Doctor -> GP '684750003': '684750001', // Agency -> Agency/Professional '684750006': '684750001' // Other -> Agency/Professional }; return referrerMapping[refererType] || '684750001'; // Default to Self if not found, but that shouldn't happen } //=========================== END - We're done, create the referral ==========================// });