1// Legacy facility validation script for NPI + facility lookup 2// Exposed as window.validateFacility so React can invoke it. 3 4// Function to get API Base URL - checks multiple sources for runtime environment variables 5function getApiBaseUrl() { 6 if (typeof window === 'undefined') { 7 return ""; 8 } 9 10 // Check window.REACT_APP_API_BASEURL (set by React in index.tsx) 11 if (window.REACT_APP_API_BASEURL) { 12 return window.REACT_APP_API_BASEURL; 13 } 14 15 // Check window.env.REACT_APP_API_BASEURL (set by env.js if available) 16 if (window.env && window.env.REACT_APP_API_BASEURL) { 17 return window.env.REACT_APP_API_BASEURL; 18 } 19 20 // Fallback to default 21 return ""; 22} 23 24async function validateFacility() { 25 // Allow React to pass values in via window.reactFacilityValidation. 26 const override = window.reactFacilityValidation || null; 27 28 let facilityName; 29 let city; 30 let country; 31 let state; 32 let npiNumber; 33 34 if (override) { 35 facilityName = (override.facilityName || "").trim(); 36 city = (override.city || "").trim(); 37 country = override.country || null; 38 state = override.state || null; 39 npiNumber = override.npiNumber || null; 40 } else { 41 facilityName = document 42 .getElementById("modalFacilityName") 43 ?.value.trim(); 44 city = document.getElementById("modalCity")?.value.trim(); 45 46 // Get the country from the select element (now contains country code as value) 47 const countrySelect = document.getElementById("modalCountry"); 48 country = countrySelect ? countrySelect.value : null; 49 50 // Get the state/province from the select element 51 const stateSelect = document.getElementById("modalState"); 52 state = stateSelect ? stateSelect.value : null; 53 if (stateSelect && stateSelect.selectedIndex > 0) { 54 state = stateSelect.value; 55 } 56 57 // Get NPI number 58 const npiInput = document.getElementById("modalCCNNumber"); 59 npiNumber = npiInput ? npiInput.value.trim() : null; 60 } 61 62 if (!country) { 63 throw new Error("Country is required but not selected"); 64 } 65 66 console.log("ð Using state for validation:", state); 67 console.log("ð Using country for validation:", country); 68 69 // These properties may be defined on some legacy context object; use window as fallback. 70 // If you have a specific context (e.g., window.facilityContext), you can adjust this. 71 const ctx = window.facilityContext || window; 72 ctx.currentCCNNumber = npiNumber; // Keep for backward compatibility 73 ctx.currentNPINumber = npiNumber; 74 ctx.npiVerified = false; // Reset NPI verification status 75 76 // Validate that facility name, city, and state are provided (NPI is optional) 77 if (!facilityName || !city || !state) { 78 alert( 79 "Please fill in all required fields: Facility Name, City, and State/Province." 80 ); 81 return; 82 } 83 84 // When NPI is provided: require exactly 10 digits 85 if (npiNumber) { 86 if (npiNumber.length !== 10 || !/^\d+$/.test(npiNumber)) { 87 const resultsDiv = document.getElementById("validationResults"); 88 if (resultsDiv) { 89 resultsDiv.style.display = "block"; 90 resultsDiv.innerHTML = ` 91 <div style="margin-top: 12px; display: flex; justify-content: center;"> 92 <div style="background-color: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; padding: 12px 16px; width: 95%; max-height: 140px; overflow-y: auto; font-family: 'Rethink Sans', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;"> 93 <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 4px; color: #b91c1c; font-weight: 600; font-size: 16px;"> 94 <span style="display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:9999px;background-color:#b91c1c;color:white;font-size:12px;">!</span> 95 <span>Invalid NPI Format</span> 96 </div> 97 <div style="height: 1px; background-color: #fecaca; margin: 4px 0 6px 0;"></div> 98 <div style="font-size: 14px; color: #b91c1c; margin-left: 26px; margin-top: 4px;"> 99 NPI must be exactly 10 digits when provided. 100 </div> 101 </div> 102 </div> 103 `; 104 } else { 105 alert("Invalid NPI format. NPI must be exactly 10 digits when provided."); 106 } 107 return; 108 } 109 } 110 111 // All validations passed - proceed with NPI lookup or organization lookup 112 const validateBtn = document.getElementById("validateBtn"); 113 const resultsDiv = document.getElementById("validationResults"); 114 115 // Store the country for use when processing validation results 116 ctx.currentValidationCountry = country; 117 118 // Helper: convert one NPI API result to facility shape for display 119 function toFacilityInfo(npiData, fallbackCity, fallbackState) { 120 const orgName = 121 (npiData.organization_name || 122 (npiData.basic && npiData.basic.organization_name) || 123 "") + ""; 124 const primaryAddress = 125 npiData.primary_address || 126 (npiData.addresses && npiData.addresses[0]) || 127 {}; 128 return { 129 site_name: orgName, 130 city: primaryAddress.city || fallbackCity, 131 state: primaryAddress.state || fallbackState, 132 facility_type: 133 (npiData.facility_type && npiData.facility_type.display_name) || 134 (npiData.primary_taxonomy && npiData.primary_taxonomy.description) || 135 "", 136 facility_type_id: npiData.facility_type && npiData.facility_type.id, 137 facility_type_slug: npiData.facility_type && npiData.facility_type.type_slug, 138 }; 139 } 140 141 // Show loading state 142 if (validateBtn) { 143 validateBtn.disabled = true; 144 } 145 if (resultsDiv) { 146 resultsDiv.style.display = "block"; 147 resultsDiv.innerHTML = ` 148 <div style="text-align: center; padding: 16px; font-family: 'Rethink Sans', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; font-size: 14px; color: #065b70;">
149 <span class="loading-spinner" style="margin-right: 8px;"></span> 150 ${npiNumber ? "Validating NPI and facility..." : "Looking up facility..."} 151 </div> 152 `; 153 } 154 155 try { 156 const API_BASE_URL = getApiBaseUrl(); 157 let npiResult; 158 159 const countryCode = 160 (typeof window !== "undefined" && 161 window.reactFacilityValidation && 162 window.reactFacilityValidation.country) || 163 ""; 164 165 if (npiNumber) { 166 // Look up by NPI number when provided 167 const npiResponse = await fetch(`${API_BASE_URL}/api/v1/npi/lookup/number`, { 168 method: "POST", 169 headers: { "Content-Type": "application/json" }, 170 body: JSON.stringify({ 171 npi_number: npiNumber, 172 city: city, 173 state: state, 174 ...(countryCode ? { country_code: String(countryCode).trim() } : {}), 175 }), 176 }); 177 npiResult = await npiResponse.json(); 178 } else { 179 // Look up by facility name + city + state when NPI is not provided 180 const orgResponse = await fetch(`${API_BASE_URL}/api/v1/npi/lookup/organization`, { 181 method: "POST", 182 headers: { "Content-Type": "application/json" }, 183 body: JSON.stringify({ 184 organization_name: facilityName, 185 city: city, 186 state: state, 187 ...(countryCode ? { country_code: String(countryCode).trim() } : {}), 188 }), 189 }); 190 npiResult = await orgResponse.json(); 191 } 192 193 if (!npiResult.success) { 194 if (resultsDiv) { 195 const isNpiPath = !!npiNumber; 196 if (isNpiPath && npiResult.error && npiResult.error.code === "LOCATION_MISMATCH") { 197 resultsDiv.innerHTML = ` 198 <div style="margin-top: 12px; display: flex; justify-content: center;"> 199 <div style="background-color: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; padding: 12px 16px; width: 95%; max-height: 140px; overflow-y: auto; font-family: 'Rethink Sans', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;"> 200 <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 4px; color: #b91c1c; font-weight: 600; font-size: 16px;"> 201 <span style="display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:9999px;background-color:#b91c1c;color:white;font-size:12px;">!</span> 202 <span>Location Mismatch</span> 203 </div> 204 <div style="height: 1px; background-color: #fecaca; margin: 4px 0 6px 0;"></div> 205 <div style="font-size: 14px; color: #b91c1c; margin-left: 26px; margin-top: 4px;"> 206 We did not find a matching NPI with the provided state and city. 207 </div> 208 <div style="font-size: 14px; color: #6b7280; margin-left: 26px; margin-top: 4px;"> 209 ${npiResult.message || "Please verify your NPI, city, and state information."} 210 </div> 211 </div> 212 </div> 213 `; 214 } else if (isNpiPath) { 215 resultsDiv.innerHTML = ` 216 <div style="margin-top: 12px; display: flex; justify-content: center;"> 217 <div style="background-color: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; padding: 12px 16px; width: 95%; max-height: 140px; overflow-y: auto; font-family: 'Rethink Sans', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;"> 218 <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 4px; color: #b91c1c; font-weight: 600; font-size: 16px;"> 219 <span style="display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:9999px;background-color:#b91c1c;color:white;font-size:12px;">!</span> 220 <span>NPI Validation Failed</span> 221 </div> 222 <div style="height: 1px; background-color: #fecaca; margin: 4px 0 6px 0;"></div> 223 <div style="font-size: 14px; color: #b91c1c; margin-left: 26px; margin-top: 4px;"> 224 ${npiResult.message || "Unable to validate NPI. Please check your NPI number and try again."} 225 </div> 226 </div> 227 </div> 228 `; 229 } else { 230 resultsDiv.innerHTML = ` 231 <div style="margin-top: 12px; display: flex; justify-content: center;"> 232 <div style="background-color: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; padding: 12px 16px; width: 95%; max-height: 140px; overflow-y: auto; font-family: 'Rethink Sans', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;"> 233 <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 4px; color: #b91c1c; font-weight: 600; font-size: 16px;">
234 <span style="display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:9999px;background-color:#b91c1c;color:white;font-size:12px;">!</span> 235 <span>Facility Lookup Failed</span> 236 </div> 237 <div style="height: 1px; background-color: #fecaca; margin: 4px 0 6px 0;"></div> 238 <div style="font-size: 14px; color: #b91c1c; margin-left: 26px; margin-top: 4px;"> 239 ${npiResult.message || "We could not find facilities matching your criteria. Try adjusting the name, city, or state."} 240 </div> 241 </div> 242 </div> 243 `; 244 } 245 } 246 return; 247 } 248 249 // Lookup successful - check if we have matching facility data 250 if ( 251 npiResult.data && 252 npiResult.data.results && 253 npiResult.data.results.length > 0 254 ) { 255 const results = npiResult.data.results; 256 const first = results[0]; 257 const facilityInfo = toFacilityInfo(first, city, state); 258 const siblingFacilities = results.slice(1).map(function (r) { 259 return toFacilityInfo(r, city, state); 260 }); 261 262 ctx.npiVerified = !!npiNumber; 263 264 const successMessage = npiNumber 265 ? (npiResult.message || "Successfully found a facility for NPI " + npiNumber + ".") 266 : (npiResult.message || "Successfully found a facility for " + facilityName + " in " + city + ", " + state + "."); 267 let facilityTypeWarningHtml = ""; 268 const primaryTaxonomyCode = 269 first.primary_taxonomy && first.primary_taxonomy.code 270 ? first.primary_taxonomy.code 271 : null; 272 const hasFacilityTypeId = 273 first.facility_type && 274 typeof first.facility_type.id !== "undefined" && 275 first.facility_type.id !== null; 276 if (!hasFacilityTypeId && primaryTaxonomyCode) { 277 facilityTypeWarningHtml = '<div style="font-size: 14px; color: #b91c1c; margin-left: 26px; margin-top: 4px;">Facility type not found for this code ' + primaryTaxonomyCode + ".</div>"; 278 } 279 280 if (resultsDiv) { 281 resultsDiv.style.display = "block"; 282 resultsDiv.innerHTML = 283 '<div style="margin-top: 12px; display: flex; justify-content: center;">' + 284 '<div style="background-color: #f0fcff; border: 1px solid #5bbfd6; border-radius: 8px; padding: 12px 16px; width: 95%; max-height: 140px; overflow-y: auto; font-family: \'Rethink Sans\', system-ui, -apple-system, BlinkMacSystemFont, \'Segoe UI\', sans-serif;">' + 285 '<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 4px; color: #065b70; font-weight: 600; font-size: 16px;">' + 286 '<span style="display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:9999px;background-color:#16a34a;color:white;font-size:12px;">â</span>' + 287 '<span>Facility Found!</span>' + 288 "</div>" + 289 '<div style="height: 1px; background-color: #d4edf3; margin: 4px 0 6px 0;"></div>' + 290 '<div style="font-size: 14px; color: #065b70; margin-left: 26px; margin-top: 4px;">' + 291 successMessage + 292 "</div>" + 293 facilityTypeWarningHtml + 294 "</div></div>"; 295 } 296 297 const validationResponse = { 298 is_valid: true, 299 message: npiNumber ? "Facility found for NPI " + npiNumber : "Facility found for " + facilityName + " in " + city + ", " + state, 300 facility: facilityInfo, 301 suggestions: [], 302 parent_facility: null, 303 sibling_facilities: siblingFacilities, 304 npi_verified: ctx.npiVerified, 305 }; 306 307 if (typeof ctx.displayValidationResults === "function") { 308 ctx.displayValidationResults(validationResponse); 309 } 310 } else if (resultsDiv) { 311 const noFacilityDetail = npiNumber ? "NPI, city, and state" : "facility name, city, and state"; 312 resultsDiv.innerHTML = 313 '<div style="margin-top: 12px; display: flex; justify-content: center;">' + 314 '<div style="background-color: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; padding: 12px 16px; width: 95%; max-height: 140px; overflow-y: auto; font-family: \'Rethink Sans\', system-ui, -apple-system, BlinkMacSystemFont, \'Segoe UI\', sans-serif;">' + 315 '<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 4px; color: #b91c1c; font-weight: 600; font-size: 16px;">' + 316 '<span style="display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:9999px;background-color:#b91c1c;color:white;font-size:12px;">!</span>' + 317 '<span>No Facility Found</span>' + 318 "</div>" + 319 '<div style="font-size: 14px; color: #b91c1c; margin-left: 26px; margin-top: 4px;">' + 320 "We did not find a matching facility for the provided " + noFacilityDetail + "." + 321 "</div></div></div>"; 322 } 323 } catch (error) { 324 console.error("Error validating facility:", error); 325 if (resultsDiv) { 326 resultsDiv.innerHTML = ` 327 <div style="margin-top: 12px; display: flex; justify-content: center;"> 328 <div style="background-color: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; padding: 12px 16px; width: 95%; max-height: 140px; overflow-y: auto; font-family: 'Rethink Sans', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;"> 329 <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 4px; color: #b91c1c; font-weight: 600; font-size: 16px;">
330 <span style="display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:9999px;background-color:#b91c1c;color:white;font-size:12px;">!</span> 331 <span>Validation Error</span> 332 </div> 333 <div style="height: 1px; background-color: #fecaca; margin: 4px 0 6px 0;"></div> 334 <div style="font-size: 14px; color: #b91c1c; margin-left: 26px; margin-top: 4px;"> 335 Unable to validate facility. Please check your internet connection and try again. 336 </div> 337 <div style="font-size: 14px; color: #6b7280; margin-left: 26px; margin-top: 4px;"> 338 Error: ${(error && error.message) || "Unknown error"} 339 </div> 340 </div> 341 </div> 342 `; 343 } 344 } finally { 345 if (validateBtn) { 346 validateBtn.disabled = false; 347 } 348 } 349} 350 351// Expose globally so React can call window.validateFacility() 352window.validateFacility = validateFacility; 353 354
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.