PageSourceSearch

https://unicomply.ai/facility-context.js?v=mups4h05-zpw4j8cc

js unicomply.ai collected 2026-10-05 14:39:02 UTC 17,173 bytes, 354 lines download raw bytes

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.