@mindbill/react 0.21.1 → 0.21.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +142 -58
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -153,7 +153,7 @@ The component includes the interaction model, not just the markup:
|
|
|
153
153
|
- a responsive two-column review form (one column on narrow screens);
|
|
154
154
|
- paste-friendly `MM/DD/YYYY` date fields and required-field asterisks;
|
|
155
155
|
- ZIP-to-city/state completion through MindBill's authenticated postal directory;
|
|
156
|
-
- complete, server-backed ICD-10 search, common-injury quick picks, and removable chips;
|
|
156
|
+
- complete, server-backed ICD-10 search with an alphabetized 100-code first page, automatic 100-code scroll paging, common-injury quick picks, and removable chips;
|
|
157
157
|
- canonical claims-administrator search through the authenticated MindBill payer directory;
|
|
158
158
|
- QME, AME, and Psych QME evaluation modes with medical-legal modifier defaults;
|
|
159
159
|
- searchable workers-comp procedure/modifier controls, medical-legal fee-schedule amounts, totals, valid manual CPT/HCPCS entry, and an automatically maintained empty line;
|
|
@@ -161,6 +161,8 @@ The component includes the interaction model, not just the markup:
|
|
|
161
161
|
|
|
162
162
|
Partners supply tenant-specific bootstrap data, one short-lived browser session callback, and the final server submission callback. Required fields, validation, ZIP lookup, ICD-10 and payer directories, service-line behavior, attachments, and submission UX stay inside `@mindbill/react`, so every integration receives the same billing workflow. Optional `diagnosisOptions`, `procedureOptions`, `modifierOptions`, and lookup callbacks extend or replace defaults when a partner has licensed or organization-specific data.
|
|
163
163
|
|
|
164
|
+
Validation is component-owned as well. A missing routing payer is highlighted immediately; Submit highlights every invalid control with a specific message, focuses and scrolls to the first problem, and continues validating as the user corrects the form.
|
|
165
|
+
|
|
164
166
|
`BILL_SUBMISSION_REQUIRED_FIELDS` and `validateBillSubmission` expose the same contract for tests and non-visual integrations. The component never creates a draft; its callback fires only when the user submits a locally valid snapshot.
|
|
165
167
|
|
|
166
168
|
`BillReviewForm` remains available for legacy integrations that already own a custom review model. New integrations should use `BillSubmissionForm`.
|
package/dist/index.d.ts
CHANGED
|
@@ -481,7 +481,7 @@ type BillSubmissionFormProps = {
|
|
|
481
481
|
fetch?: typeof globalThis.fetch;
|
|
482
482
|
onSearchClaimsAdministrators?: (query: string, claimNumber?: string) => Promise<BillReviewPayer$1[]>;
|
|
483
483
|
diagnosisOptions?: BillSubmissionDiagnosisOption[];
|
|
484
|
-
onSearchDiagnoses?: (query: string) => Promise<BillSubmissionDiagnosisOption[]>;
|
|
484
|
+
onSearchDiagnoses?: (query: string, limit?: number, offset?: number) => Promise<BillSubmissionDiagnosisOption[]>;
|
|
485
485
|
onLookupPostalCode?: (postalCode: string) => Promise<BillSubmissionPostalPlace | null>;
|
|
486
486
|
procedureOptions?: BillSubmissionProcedureOption[];
|
|
487
487
|
modifierOptions?: BillSubmissionModifierOption[];
|
package/dist/index.js
CHANGED
|
@@ -1320,6 +1320,7 @@ var documentLabels = {
|
|
|
1320
1320
|
var MAX_PDF_BYTES = 25 * 1024 * 1024;
|
|
1321
1321
|
var MAX_UPLOAD_BYTES = 100 * 1024 * 1024;
|
|
1322
1322
|
var MAX_DOCUMENTS = 25;
|
|
1323
|
+
var DIAGNOSIS_PAGE_SIZE = 100;
|
|
1323
1324
|
var EMPTY_ATTACHMENTS = [];
|
|
1324
1325
|
function previewUploadedPdf(file) {
|
|
1325
1326
|
const url = URL.createObjectURL(file);
|
|
@@ -1389,19 +1390,19 @@ function cloneBill(bill) {
|
|
|
1389
1390
|
}
|
|
1390
1391
|
function validateBillSubmission(bill) {
|
|
1391
1392
|
const errors = {};
|
|
1392
|
-
const required = (path, value) => {
|
|
1393
|
-
if (typeof value !== "string" || !value.trim()) errors[path] =
|
|
1393
|
+
const required = (path, value, message) => {
|
|
1394
|
+
if (typeof value !== "string" || !value.trim()) errors[path] = message;
|
|
1394
1395
|
};
|
|
1395
|
-
required("patient.firstName", bill.patient.firstName);
|
|
1396
|
-
required("patient.lastName", bill.patient.lastName);
|
|
1397
|
-
required("patient.dateOfBirth", bill.patient.dateOfBirth);
|
|
1398
|
-
required("patient.address.line1", bill.patient.address.line1);
|
|
1399
|
-
required("patient.address.city", bill.patient.address.city);
|
|
1400
|
-
required("patient.address.state", bill.patient.address.state);
|
|
1401
|
-
required("patient.address.postalCode", bill.patient.address.postalCode);
|
|
1402
|
-
required("claim.claimNumber", bill.claim.claimNumber);
|
|
1403
|
-
required("claim.claimsAdministrator", bill.claim.claimsAdministrator?.name);
|
|
1404
|
-
required("service.date", bill.service.date);
|
|
1396
|
+
required("patient.firstName", bill.patient.firstName, "Enter the patient's first name.");
|
|
1397
|
+
required("patient.lastName", bill.patient.lastName, "Enter the patient's last name.");
|
|
1398
|
+
required("patient.dateOfBirth", bill.patient.dateOfBirth, "Enter the patient's date of birth.");
|
|
1399
|
+
required("patient.address.line1", bill.patient.address.line1, "Enter the patient's street address.");
|
|
1400
|
+
required("patient.address.city", bill.patient.address.city, "Enter the patient's city.");
|
|
1401
|
+
required("patient.address.state", bill.patient.address.state, "Enter the patient's 2-letter state code.");
|
|
1402
|
+
required("patient.address.postalCode", bill.patient.address.postalCode, "Enter the patient's ZIP code.");
|
|
1403
|
+
required("claim.claimNumber", bill.claim.claimNumber, "Enter the claim number.");
|
|
1404
|
+
required("claim.claimsAdministrator", bill.claim.claimsAdministrator?.name, "Select a claims administrator so MindBill can route this bill.");
|
|
1405
|
+
required("service.date", bill.service.date, "Enter the date of service.");
|
|
1405
1406
|
if (bill.claim.claimsAdministrator?.name && !bill.claim.claimsAdministrator.id) {
|
|
1406
1407
|
errors["claim.claimsAdministrator"] = "Select a claims administrator from the payer directory.";
|
|
1407
1408
|
}
|
|
@@ -1410,7 +1411,7 @@ function validateBillSubmission(bill) {
|
|
|
1410
1411
|
const lines = submittedLines(bill.serviceLines);
|
|
1411
1412
|
if (!lines.length) errors.serviceLines = "Add at least one service line";
|
|
1412
1413
|
lines.forEach((line, index) => {
|
|
1413
|
-
required(`serviceLines.${index}.code`, line.code);
|
|
1414
|
+
required(`serviceLines.${index}.code`, line.code, "Select a procedure code.");
|
|
1414
1415
|
if (!Number.isInteger(line.units) || (line.units ?? 0) < 1) errors[`serviceLines.${index}.units`] = "Enter at least 1 unit";
|
|
1415
1416
|
if (bill.billingMode === "professional" && (!Number.isFinite(line.charge) || (line.charge ?? 0) < 0)) errors[`serviceLines.${index}.charge`] = "Enter the billed charge";
|
|
1416
1417
|
});
|
|
@@ -1419,25 +1420,25 @@ function validateBillSubmission(bill) {
|
|
|
1419
1420
|
var css = `
|
|
1420
1421
|
.mbsf{display:grid;gap:20px;color:var(--mb-text);font-family:var(--mb-font);font-size:15px}.mbsf *{box-sizing:border-box}
|
|
1421
1422
|
.mbsf-head,.mbsf-section-head,.mbsf-attach-row,.mbsf-actions{display:flex;align-items:center;justify-content:space-between;gap:16px}.mbsf-title{margin:0;font-size:24px}.mbsf-copy,.mbsf-help{color:var(--mb-muted);margin:5px 0 0}.mbsf-required{font-size:13px;color:var(--mb-muted);white-space:nowrap}.mbsf-star,.mbsf-error{color:var(--mb-danger)}
|
|
1422
|
-
.mbsf-card{min-width:0;margin:0;padding:24px;border:1px solid var(--mb-border);border-radius:var(--mb-radius);background:var(--mb-surface);box-shadow:var(--mb-shadow)}.mbsf-legend{padding:0 10px;font-size:18px;font-weight:760}.mbsf-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:18px 24px}.mbsf-span{grid-column:1/-1}.mbsf-field{display:grid;align-content:start;gap:7px;min-width:0}.mbsf-label{font-weight:680}.mbsf-input,.mbsf-select{width:100%;min-height:46px;padding:10px 12px;border:1px solid var(--mb-border);border-radius:var(--mb-control-radius);background:var(--mb-input);color:var(--mb-text);font:inherit}.mbsf-input:focus,.mbsf-select:focus{outline:3px solid color-mix(in srgb,var(--mb-accent) 22%,transparent);border-color:var(--mb-accent)}
|
|
1423
|
-
.mbsf-combo{position:relative}.mbsf-menu{position:absolute;z-index:20;top:calc(100% + 5px);left:0;right:0;max-height:min(
|
|
1423
|
+
.mbsf-card{min-width:0;margin:0;padding:24px;border:1px solid var(--mb-border);border-radius:var(--mb-radius);background:var(--mb-surface);box-shadow:var(--mb-shadow)}.mbsf-legend{padding:0 10px;font-size:18px;font-weight:760}.mbsf-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:18px 24px}.mbsf-span{grid-column:1/-1}.mbsf-field{display:grid;align-content:start;gap:7px;min-width:0}.mbsf-label{font-weight:680}.mbsf-input,.mbsf-select{width:100%;min-height:46px;padding:10px 12px;border:1px solid var(--mb-border);border-radius:var(--mb-control-radius);background:var(--mb-input);color:var(--mb-text);font:inherit}.mbsf-input:focus,.mbsf-select:focus{outline:3px solid color-mix(in srgb,var(--mb-accent) 22%,transparent);border-color:var(--mb-accent)}.mbsf-field[data-invalid=true] .mbsf-input,.mbsf-field[data-invalid=true] .mbsf-select,.mbsf-invalid-control .mbsf-input{border-color:var(--mb-danger);background:color-mix(in srgb,var(--mb-danger) 4%,var(--mb-input))}.mbsf-field[data-invalid=true] .mbsf-input:focus,.mbsf-field[data-invalid=true] .mbsf-select:focus{outline-color:color-mix(in srgb,var(--mb-danger) 24%,transparent)}
|
|
1424
|
+
.mbsf-combo{position:relative}.mbsf-menu{position:absolute;z-index:20;top:calc(100% + 5px);left:0;right:0;max-height:min(360px,46vh);overflow:auto;overscroll-behavior:contain;padding:7px;border:1px solid var(--mb-border);border-radius:12px;background:var(--mb-surface);box-shadow:0 14px 35px rgba(17,38,49,.16)}.mbsf-option{display:grid;width:100%;gap:2px;padding:10px;border:0;border-radius:8px;background:transparent;color:var(--mb-text);font:inherit;text-align:left;cursor:pointer}.mbsf-option:hover,.mbsf-option:focus{background:color-mix(in srgb,var(--mb-accent) 9%,var(--mb-surface))}.mbsf-option small{color:var(--mb-muted)}.mbsf-menu-status{padding:12px;text-align:center;color:var(--mb-muted);font-size:13px}
|
|
1424
1425
|
.mbsf-chips,.mbsf-quick-picks{display:flex;flex-wrap:wrap;gap:7px;margin-bottom:8px}.mbsf-chip,.mbsf-quick-pick{display:inline-flex;align-items:center;gap:7px;padding:5px 9px;border:1px solid var(--mb-border);border-radius:999px;background:var(--mb-input)}.mbsf-chip button{border:0;background:transparent;color:var(--mb-muted);cursor:pointer;font:inherit}.mbsf-quick-pick{color:var(--mb-text);font:inherit;cursor:pointer}.mbsf-quick-pick[data-selected=true]{border-color:var(--mb-accent);color:var(--mb-accent)}
|
|
1425
1426
|
.mbsf-segments{display:grid;grid-template-columns:repeat(3,1fr);border:1px solid var(--mb-border);border-radius:var(--mb-control-radius);overflow:hidden}.mbsf-segment{min-height:44px;border:0;border-right:1px solid var(--mb-border);background:var(--mb-input);color:var(--mb-text);font:inherit;font-weight:700;cursor:pointer}.mbsf-segment:last-child{border-right:0}.mbsf-segment[aria-pressed=true]{background:var(--mb-accent);color:var(--mb-accent-contrast)}
|
|
1426
|
-
.mbsf-lines{margin-top:18px;border:1px solid var(--mb-border);border-radius:var(--mb-control-radius);overflow:visible}.mbsf-line-head,.mbsf-line{display:grid;grid-template-columns:minmax(190px,1.05fr) minmax(220px,1.5fr) 100px 120px 42px;gap:12px;align-items:start;padding:12px}.mbsf-line-head{color:var(--mb-muted);font-size:13px;font-weight:700;border-bottom:1px solid var(--mb-border)}.mbsf-line{border-bottom:1px solid var(--mb-border)}.mbsf-line:last-child{border-bottom:0}.mbsf-money{padding-top:12px;text-align:right;font-variant-numeric:tabular-nums}.mbsf-total{display:flex;justify-content:flex-end;gap:45px;padding:16px 56px 16px 16px;font-size:17px;font-weight:760}
|
|
1427
|
+
.mbsf-lines{margin-top:18px;border:1px solid var(--mb-border);border-radius:var(--mb-control-radius);overflow:visible}.mbsf-lines[data-invalid=true]{border-color:var(--mb-danger)}.mbsf-line-head,.mbsf-line{display:grid;grid-template-columns:minmax(190px,1.05fr) minmax(220px,1.5fr) 100px 120px 42px;gap:12px;align-items:start;padding:12px}.mbsf-line-head{color:var(--mb-muted);font-size:13px;font-weight:700;border-bottom:1px solid var(--mb-border)}.mbsf-line{border-bottom:1px solid var(--mb-border)}.mbsf-line:last-child{border-bottom:0}.mbsf-line [data-invalid=true] .mbsf-input{border-color:var(--mb-danger);background:color-mix(in srgb,var(--mb-danger) 4%,var(--mb-input))}.mbsf-money{padding-top:12px;text-align:right;font-variant-numeric:tabular-nums}.mbsf-total{display:flex;justify-content:flex-end;gap:45px;padding:16px 56px 16px 16px;font-size:17px;font-weight:760}
|
|
1427
1428
|
.mbsf-icon-btn{width:40px;height:42px;border:0;background:transparent;color:var(--mb-text);font-size:22px;cursor:pointer}.mbsf-secondary{min-height:40px;padding:8px 14px;border:1px solid var(--mb-border);border-radius:var(--mb-control-radius);background:var(--mb-surface);color:var(--mb-text);font:inherit;font-weight:680;cursor:pointer}.mbsf-attach-list{display:grid;gap:10px;margin-bottom:18px}.mbsf-attach-row{padding:14px;border:1px solid var(--mb-border);border-radius:var(--mb-control-radius)}.mbsf-attach-row[data-auto=true]{border-color:color-mix(in srgb,#159447 45%,var(--mb-border));background:color-mix(in srgb,#159447 5%,var(--mb-surface))}.mbsf-attach-main{display:flex;align-items:center;gap:12px;min-width:0;flex:1}.mbsf-attach-actions{display:flex;align-items:center;gap:6px;flex:0 0 auto}.mbsf-file{min-width:0}.mbsf-file strong{overflow-wrap:anywhere}.mbsf-badge{display:inline-block;margin-left:8px;padding:2px 7px;border:1px solid var(--mb-border);border-radius:7px;color:var(--mb-muted);font-size:12px;font-weight:600}.mbsf-drop{display:grid;width:100%;place-items:center;min-height:210px;padding:30px;border:2px dashed color-mix(in srgb,var(--mb-muted) 55%,transparent);border-radius:var(--mb-control-radius);background:color-mix(in srgb,var(--mb-accent) 3%,var(--mb-surface));color:var(--mb-text);font:inherit;text-align:center;cursor:pointer}.mbsf-drop[data-active=true]{border-color:var(--mb-accent);background:color-mix(in srgb,var(--mb-accent) 10%,var(--mb-surface))}.mbsf-alert{padding:12px 14px;border-radius:var(--mb-control-radius);background:color-mix(in srgb,var(--mb-danger) 10%,transparent);color:var(--mb-danger)}.mbsf-actions{justify-content:flex-end}.mbsf-submit{min-width:180px;min-height:48px;padding:11px 24px;border:0;border-radius:var(--mb-control-radius);background:var(--mb-accent);color:var(--mb-accent-contrast);font:inherit;font-weight:780;cursor:pointer}
|
|
1428
1429
|
@media(max-width:820px){.mbsf{gap:16px}.mbsf-grid{grid-template-columns:1fr}.mbsf-span{grid-column:auto}.mbsf-card{padding:18px 16px}.mbsf-line-head{display:none}.mbsf-line{position:relative;display:grid;grid-template-columns:minmax(0,1fr) 86px;gap:14px;padding:18px 16px}.mbsf-line>div:before{display:block;margin-bottom:6px;color:var(--mb-muted);font-size:12px;font-weight:700;content:attr(data-label)}.mbsf-line>div:nth-child(1),.mbsf-line>div:nth-child(2){grid-column:1/-1}.mbsf-money{align-self:end;padding:0 0 12px;text-align:right}.mbsf-line .mbsf-icon-btn{position:absolute;right:8px;bottom:3px}.mbsf-total{padding:16px 18px;gap:24px}.mbsf-head{align-items:flex-start}.mbsf-segments{grid-template-columns:repeat(3,minmax(0,1fr))}.mbsf-segment{min-width:0;padding:8px 4px;border-right:1px solid var(--mb-border);border-bottom:0;font-size:13px}.mbsf-segment:last-child{border-right:0}.mbsf-attach-row{align-items:flex-start;flex-wrap:wrap}.mbsf-attach-main{align-items:flex-start}.mbsf-attach-actions{margin-left:auto}.mbsf-drop{min-height:190px;padding:24px 18px}.mbsf-actions{position:sticky;bottom:86px;z-index:10}.mbsf-submit{width:100%}}
|
|
1429
1430
|
`;
|
|
1430
1431
|
function RequiredMark() {
|
|
1431
1432
|
return /* @__PURE__ */ jsx3("span", { className: "mbsf-star", children: " *" });
|
|
1432
1433
|
}
|
|
1433
|
-
function Field2({ label, required, error, span, children }) {
|
|
1434
|
-
return /* @__PURE__ */ jsxs3("label", { className: `mbsf-field${span ? " mbsf-span" : ""}`, children: [
|
|
1434
|
+
function Field2({ label, required, error, path, span, children }) {
|
|
1435
|
+
return /* @__PURE__ */ jsxs3("label", { className: `mbsf-field${span ? " mbsf-span" : ""}`, "data-field-path": path, "data-invalid": Boolean(error), children: [
|
|
1435
1436
|
/* @__PURE__ */ jsxs3("span", { className: "mbsf-label", children: [
|
|
1436
1437
|
label,
|
|
1437
1438
|
required ? /* @__PURE__ */ jsx3(RequiredMark, {}) : null
|
|
1438
1439
|
] }),
|
|
1439
1440
|
children,
|
|
1440
|
-
error ? /* @__PURE__ */ jsx3("small", { className: "mbsf-error", children: error }) : null
|
|
1441
|
+
error ? /* @__PURE__ */ jsx3("small", { className: "mbsf-error", role: "alert", children: error }) : null
|
|
1441
1442
|
] });
|
|
1442
1443
|
}
|
|
1443
1444
|
function TextDateInput({ value, onChange, disabled, required, ariaLabel }) {
|
|
@@ -1459,7 +1460,7 @@ function TextDateInput({ value, onChange, disabled, required, ariaLabel }) {
|
|
|
1459
1460
|
invalid ? /* @__PURE__ */ jsx3("small", { className: "mbsf-error", children: "Use MM/DD/YYYY" }) : null
|
|
1460
1461
|
] });
|
|
1461
1462
|
}
|
|
1462
|
-
function ComboBox({ value, placeholder, options, disabled, loading, onQuery, onSelect, createOption, ariaLabel }) {
|
|
1463
|
+
function ComboBox({ value, placeholder, options, disabled, loading, loadingMore, invalid, onOpen, onQuery, onEndReached, onSelect, createOption, ariaLabel }) {
|
|
1463
1464
|
const [open, setOpen] = useState2(false);
|
|
1464
1465
|
const [query, setQuery] = useState2("");
|
|
1465
1466
|
const visible = useMemo2(() => {
|
|
@@ -1470,24 +1471,50 @@ function ComboBox({ value, placeholder, options, disabled, loading, onQuery, onS
|
|
|
1470
1471
|
}, [createOption, options, query]);
|
|
1471
1472
|
const showMenu = open && (loading || visible.length > 0 || query.trim().length >= 2);
|
|
1472
1473
|
return /* @__PURE__ */ jsxs3("div", { className: "mbsf-combo", children: [
|
|
1473
|
-
/* @__PURE__ */ jsx3("input", { className: "mbsf-input", role: "combobox", "aria-label": ariaLabel, "aria-expanded": showMenu, autoComplete: "off", disabled, placeholder, value: open ? query : value, onFocus: () => {
|
|
1474
|
+
/* @__PURE__ */ jsx3("input", { className: "mbsf-input", role: "combobox", "aria-label": ariaLabel, "aria-invalid": invalid, "aria-expanded": showMenu, autoComplete: "off", disabled, placeholder, value: open ? query : value, onFocus: () => {
|
|
1474
1475
|
setOpen(true);
|
|
1475
1476
|
setQuery("");
|
|
1477
|
+
onOpen?.();
|
|
1476
1478
|
}, onChange: (event) => {
|
|
1477
1479
|
setOpen(true);
|
|
1478
1480
|
setQuery(event.target.value);
|
|
1479
1481
|
onQuery?.(event.target.value);
|
|
1480
1482
|
}, onBlur: () => setTimeout(() => setOpen(false), 120) }),
|
|
1481
|
-
showMenu ? /* @__PURE__ */
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
setQuery("");
|
|
1483
|
+
showMenu ? /* @__PURE__ */ jsxs3("div", { className: "mbsf-menu", role: "listbox", onScroll: (event) => {
|
|
1484
|
+
const menu = event.currentTarget;
|
|
1485
|
+
if (menu.scrollHeight - menu.scrollTop - menu.clientHeight < 120) onEndReached?.();
|
|
1485
1486
|
}, children: [
|
|
1486
|
-
/* @__PURE__ */ jsx3("
|
|
1487
|
-
|
|
1488
|
-
|
|
1487
|
+
loading && !visible.length ? /* @__PURE__ */ jsx3("div", { className: "mbsf-menu-status", children: "Loading codes\u2026" }) : visible.length ? visible.map((option) => /* @__PURE__ */ jsxs3("button", { className: "mbsf-option", type: "button", role: "option", onMouseDown: (event) => event.preventDefault(), onClick: () => {
|
|
1488
|
+
onSelect(option);
|
|
1489
|
+
setOpen(false);
|
|
1490
|
+
setQuery("");
|
|
1491
|
+
}, children: [
|
|
1492
|
+
/* @__PURE__ */ jsx3("strong", { children: option.label }),
|
|
1493
|
+
option.detail ? /* @__PURE__ */ jsx3("small", { children: option.detail }) : null
|
|
1494
|
+
] }, option.id)) : /* @__PURE__ */ jsx3("div", { className: "mbsf-option", children: "No matches" }),
|
|
1495
|
+
loadingMore ? /* @__PURE__ */ jsx3("div", { className: "mbsf-menu-status", children: "Loading more\u2026" }) : null
|
|
1496
|
+
] }) : null
|
|
1489
1497
|
] });
|
|
1490
1498
|
}
|
|
1499
|
+
function focusFirstInvalid(form) {
|
|
1500
|
+
const field = form.querySelector('[data-invalid="true"]');
|
|
1501
|
+
if (!field) return;
|
|
1502
|
+
const control = field.querySelector('input,select,textarea,button,[tabindex]:not([tabindex="-1"])');
|
|
1503
|
+
control?.focus({ preventScroll: true });
|
|
1504
|
+
const fieldRect = field.getBoundingClientRect();
|
|
1505
|
+
let scrollParent = field.parentElement;
|
|
1506
|
+
while (scrollParent) {
|
|
1507
|
+
const style = window.getComputedStyle(scrollParent);
|
|
1508
|
+
if (/(auto|scroll)/.test(style.overflowY) && scrollParent.scrollHeight > scrollParent.clientHeight) break;
|
|
1509
|
+
scrollParent = scrollParent.parentElement;
|
|
1510
|
+
}
|
|
1511
|
+
if (scrollParent) {
|
|
1512
|
+
const parentRect = scrollParent.getBoundingClientRect();
|
|
1513
|
+
scrollParent.scrollTo({ top: Math.max(0, scrollParent.scrollTop + fieldRect.top - parentRect.top - 24), behavior: "smooth" });
|
|
1514
|
+
} else {
|
|
1515
|
+
window.scrollTo({ top: Math.max(0, window.scrollY + fieldRect.top - 24), behavior: "smooth" });
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1491
1518
|
function customProcedureOption(query) {
|
|
1492
1519
|
const code = query.trim().toUpperCase().replace(/\s+/g, "");
|
|
1493
1520
|
if (!/^(?:\d{5}|[A-Z]\d{4}|ML(?:20[0-5]|PRR))$/.test(code)) return null;
|
|
@@ -1498,6 +1525,9 @@ function mergeOptions(defaults, supplied) {
|
|
|
1498
1525
|
supplied?.forEach((item) => map.set(item.code.toUpperCase(), item));
|
|
1499
1526
|
return [...map.values()];
|
|
1500
1527
|
}
|
|
1528
|
+
function mergeDiagnosisOptions(options) {
|
|
1529
|
+
return [...new Map(options.map((item) => [item.code.toUpperCase(), item])).values()].sort((left, right) => left.code.localeCompare(right.code, void 0, { numeric: true }));
|
|
1530
|
+
}
|
|
1501
1531
|
function BillSubmissionForm({
|
|
1502
1532
|
initialBill,
|
|
1503
1533
|
attachments = EMPTY_ATTACHMENTS,
|
|
@@ -1525,15 +1555,22 @@ function BillSubmissionForm({
|
|
|
1525
1555
|
const [removedSourceIds, setRemovedSourceIds] = useState2([]);
|
|
1526
1556
|
const [uploads, setUploads] = useState2([]);
|
|
1527
1557
|
const [errors, setErrors] = useState2({});
|
|
1558
|
+
const [validationActive, setValidationActive] = useState2(false);
|
|
1528
1559
|
const [formError, setFormError] = useState2(null);
|
|
1529
1560
|
const [submitting, setSubmitting] = useState2(false);
|
|
1530
1561
|
const [payerResults, setPayerResults] = useState2([]);
|
|
1531
1562
|
const [payerLoading, setPayerLoading] = useState2(false);
|
|
1532
|
-
const [diagnosisResults, setDiagnosisResults] = useState2(
|
|
1563
|
+
const [diagnosisResults, setDiagnosisResults] = useState2([]);
|
|
1533
1564
|
const [diagnosisLoading, setDiagnosisLoading] = useState2(false);
|
|
1565
|
+
const [diagnosisLoadingMore, setDiagnosisLoadingMore] = useState2(false);
|
|
1566
|
+
const [diagnosisQuery, setDiagnosisQuery] = useState2(null);
|
|
1567
|
+
const [diagnosisHasMore, setDiagnosisHasMore] = useState2(true);
|
|
1534
1568
|
const [postalStatus, setPostalStatus] = useState2(null);
|
|
1535
1569
|
const [dragActive, setDragActive] = useState2(false);
|
|
1570
|
+
const formRef = useRef2(null);
|
|
1536
1571
|
const fileInput = useRef2(null);
|
|
1572
|
+
const diagnosisRequest = useRef2(0);
|
|
1573
|
+
const diagnosisAppendPending = useRef2(false);
|
|
1537
1574
|
const procedures = useMemo2(() => mergeOptions(DEFAULT_BILL_SUBMISSION_PROCEDURES, procedureOptions), [procedureOptions]);
|
|
1538
1575
|
const modifiers = useMemo2(() => mergeOptions(DEFAULT_BILL_SUBMISSION_MODIFIERS, modifierOptions), [modifierOptions]);
|
|
1539
1576
|
const [evaluationType, setEvaluationType] = useState2(() => initialBill.renderingProvider?.isAme ? "ame" : initialBill.renderingProvider?.specialty?.toLowerCase().includes("psych") ? "psych_qme" : "qme");
|
|
@@ -1545,8 +1582,17 @@ function BillSubmissionForm({
|
|
|
1545
1582
|
setRemovedSourceIds([]);
|
|
1546
1583
|
setUploads([]);
|
|
1547
1584
|
setErrors({});
|
|
1585
|
+
setValidationActive(false);
|
|
1548
1586
|
setFormError(null);
|
|
1587
|
+
setDiagnosisResults([]);
|
|
1588
|
+
setDiagnosisQuery(null);
|
|
1589
|
+
setDiagnosisHasMore(true);
|
|
1590
|
+
diagnosisRequest.current += 1;
|
|
1591
|
+
diagnosisAppendPending.current = false;
|
|
1549
1592
|
}, [initialBill, attachments]);
|
|
1593
|
+
useEffect2(() => {
|
|
1594
|
+
if (validationActive) setErrors(validateBillSubmission(bill).fieldErrors);
|
|
1595
|
+
}, [bill, validationActive]);
|
|
1550
1596
|
const addFiles = useCallback((fileList) => {
|
|
1551
1597
|
const files = Array.from(fileList);
|
|
1552
1598
|
if (!files.length) return;
|
|
@@ -1610,17 +1656,43 @@ function BillSubmissionForm({
|
|
|
1610
1656
|
}
|
|
1611
1657
|
void search(query, bill.claim.claimNumber).then(setPayerResults).catch((caught) => setFormError(caught instanceof Error ? caught.message : "Claims administrator search is unavailable.")).finally(() => setPayerLoading(false));
|
|
1612
1658
|
};
|
|
1613
|
-
const
|
|
1659
|
+
const loadDiagnoses = (query, append = false) => {
|
|
1614
1660
|
const trimmed = query.trim();
|
|
1615
|
-
|
|
1616
|
-
|
|
1661
|
+
const search = onSearchDiagnoses ?? referenceClient?.searchDiagnosisCodes;
|
|
1662
|
+
if (!search) {
|
|
1663
|
+
setDiagnosisResults(mergeDiagnosisOptions(diagnosisOptions));
|
|
1664
|
+
setDiagnosisHasMore(false);
|
|
1665
|
+
setDiagnosisQuery(trimmed);
|
|
1617
1666
|
return;
|
|
1618
1667
|
}
|
|
1619
|
-
const
|
|
1620
|
-
if (!
|
|
1621
|
-
|
|
1622
|
-
|
|
1668
|
+
const continuing = append && diagnosisQuery === trimmed;
|
|
1669
|
+
if (continuing && (!diagnosisHasMore || diagnosisAppendPending.current)) return;
|
|
1670
|
+
const offset = continuing ? diagnosisResults.length : 0;
|
|
1671
|
+
const request = ++diagnosisRequest.current;
|
|
1672
|
+
if (continuing) {
|
|
1673
|
+
diagnosisAppendPending.current = true;
|
|
1674
|
+
setDiagnosisLoadingMore(true);
|
|
1675
|
+
} else {
|
|
1676
|
+
setDiagnosisLoading(true);
|
|
1677
|
+
setDiagnosisLoadingMore(false);
|
|
1678
|
+
setDiagnosisQuery(trimmed);
|
|
1679
|
+
setDiagnosisHasMore(true);
|
|
1680
|
+
}
|
|
1681
|
+
void search(trimmed, DIAGNOSIS_PAGE_SIZE, offset).then((page) => {
|
|
1682
|
+
if (request !== diagnosisRequest.current) return;
|
|
1683
|
+
setDiagnosisResults((current) => mergeDiagnosisOptions(continuing ? [...current, ...page] : page));
|
|
1684
|
+
setDiagnosisHasMore(page.length >= DIAGNOSIS_PAGE_SIZE);
|
|
1685
|
+
}).catch(() => {
|
|
1686
|
+
if (request === diagnosisRequest.current && !continuing) setDiagnosisResults([]);
|
|
1687
|
+
}).finally(() => {
|
|
1688
|
+
if (request !== diagnosisRequest.current) return;
|
|
1689
|
+
diagnosisAppendPending.current = false;
|
|
1690
|
+
setDiagnosisLoading(false);
|
|
1691
|
+
setDiagnosisLoadingMore(false);
|
|
1692
|
+
});
|
|
1623
1693
|
};
|
|
1694
|
+
const searchDiagnoses = (query) => loadDiagnoses(query);
|
|
1695
|
+
const diagnosisChoices = useMemo2(() => mergeDiagnosisOptions([...diagnosisOptions, ...diagnosisResults]), [diagnosisOptions, diagnosisResults]);
|
|
1624
1696
|
const updatePostalCode = (postalCode) => {
|
|
1625
1697
|
setAddress({ postalCode });
|
|
1626
1698
|
setPostalStatus(null);
|
|
@@ -1645,9 +1717,18 @@ function BillSubmissionForm({
|
|
|
1645
1717
|
return charge == null ? line : { ...line, charge };
|
|
1646
1718
|
}) };
|
|
1647
1719
|
const validation = validateBillSubmission(clean);
|
|
1720
|
+
setValidationActive(true);
|
|
1648
1721
|
setErrors(validation.fieldErrors);
|
|
1649
1722
|
setFormError(null);
|
|
1650
|
-
if (!validation.valid)
|
|
1723
|
+
if (!validation.valid) {
|
|
1724
|
+
const count = Object.keys(validation.fieldErrors).length;
|
|
1725
|
+
const first = Object.values(validation.fieldErrors)[0];
|
|
1726
|
+
setFormError(`Fix ${count} highlighted field${count === 1 ? "" : "s"} before submitting. ${first}`);
|
|
1727
|
+
window.requestAnimationFrame(() => {
|
|
1728
|
+
if (formRef.current) focusFirstInvalid(formRef.current);
|
|
1729
|
+
});
|
|
1730
|
+
return;
|
|
1731
|
+
}
|
|
1651
1732
|
if (selectedIds.length + uploads.length > MAX_DOCUMENTS) return setFormError(`A bill can include at most ${MAX_DOCUMENTS} attachments.`);
|
|
1652
1733
|
setSubmitting(true);
|
|
1653
1734
|
try {
|
|
@@ -1658,7 +1739,8 @@ function BillSubmissionForm({
|
|
|
1658
1739
|
setSubmitting(false);
|
|
1659
1740
|
}
|
|
1660
1741
|
}
|
|
1661
|
-
|
|
1742
|
+
const claimsAdministratorError = errors["claim.claimsAdministrator"] ?? (!bill.claim.claimsAdministrator?.id ? "Required for routing \u2014 search and select a claims administrator." : void 0);
|
|
1743
|
+
return /* @__PURE__ */ jsxs3("form", { ref: formRef, className: `${className} mbsf`, style: { ...mindBillAppearanceStyle(appearance), ...style }, onSubmit: (event) => {
|
|
1662
1744
|
event.preventDefault();
|
|
1663
1745
|
void submit();
|
|
1664
1746
|
}, noValidate: true, children: [
|
|
@@ -1676,29 +1758,29 @@ function BillSubmissionForm({
|
|
|
1676
1758
|
/* @__PURE__ */ jsxs3("fieldset", { className: "mbsf-card", disabled: locked, children: [
|
|
1677
1759
|
/* @__PURE__ */ jsx3("legend", { className: "mbsf-legend", children: "Patient" }),
|
|
1678
1760
|
/* @__PURE__ */ jsxs3("div", { className: "mbsf-grid", children: [
|
|
1679
|
-
/* @__PURE__ */ jsx3(Field2, { label: "First name", required: true, error: errors["patient.firstName"], children: text(bill.patient.firstName, (firstName) => setBill((c) => ({ ...c, patient: { ...c.patient, firstName } }))) }),
|
|
1680
|
-
/* @__PURE__ */ jsx3(Field2, { label: "Last name", required: true, error: errors["patient.lastName"], children: text(bill.patient.lastName, (lastName) => setBill((c) => ({ ...c, patient: { ...c.patient, lastName } }))) }),
|
|
1761
|
+
/* @__PURE__ */ jsx3(Field2, { path: "patient.firstName", label: "First name", required: true, error: errors["patient.firstName"], children: text(bill.patient.firstName, (firstName) => setBill((c) => ({ ...c, patient: { ...c.patient, firstName } }))) }),
|
|
1762
|
+
/* @__PURE__ */ jsx3(Field2, { path: "patient.lastName", label: "Last name", required: true, error: errors["patient.lastName"], children: text(bill.patient.lastName, (lastName) => setBill((c) => ({ ...c, patient: { ...c.patient, lastName } }))) }),
|
|
1681
1763
|
/* @__PURE__ */ jsx3(Field2, { label: "Middle name", children: text(bill.patient.middleName, (middleName) => setBill((c) => ({ ...c, patient: { ...c.patient, middleName } }))) }),
|
|
1682
1764
|
/* @__PURE__ */ jsx3(Field2, { label: "Phone (optional)", children: text(bill.patient.phone, (phone) => setBill((c) => ({ ...c, patient: { ...c.patient, phone } })), { type: "tel" }) }),
|
|
1683
|
-
/* @__PURE__ */ jsx3(Field2, { label: "Date of birth", required: true, error: errors["patient.dateOfBirth"], children: /* @__PURE__ */ jsx3(TextDateInput, { ariaLabel: "Date of birth", value: bill.patient.dateOfBirth, disabled: locked, required: true, onChange: (dateOfBirth) => setBill((c) => ({ ...c, patient: { ...c.patient, dateOfBirth } })) }) }),
|
|
1684
|
-
/* @__PURE__ */ jsx3(Field2, { label: "Address", required: true, span: true, error: errors["patient.address.line1"], children: text(bill.patient.address.line1, (line1) => setAddress({ line1 }), { placeholder: "Street address" }) }),
|
|
1685
|
-
/* @__PURE__ */ jsxs3(Field2, { label: "ZIP", required: true, error: errors["patient.address.postalCode"], children: [
|
|
1765
|
+
/* @__PURE__ */ jsx3(Field2, { path: "patient.dateOfBirth", label: "Date of birth", required: true, error: errors["patient.dateOfBirth"], children: /* @__PURE__ */ jsx3(TextDateInput, { ariaLabel: "Date of birth", value: bill.patient.dateOfBirth, disabled: locked, required: true, onChange: (dateOfBirth) => setBill((c) => ({ ...c, patient: { ...c.patient, dateOfBirth } })) }) }),
|
|
1766
|
+
/* @__PURE__ */ jsx3(Field2, { path: "patient.address.line1", label: "Address", required: true, span: true, error: errors["patient.address.line1"], children: text(bill.patient.address.line1, (line1) => setAddress({ line1 }), { placeholder: "Street address" }) }),
|
|
1767
|
+
/* @__PURE__ */ jsxs3(Field2, { path: "patient.address.postalCode", label: "ZIP", required: true, error: errors["patient.address.postalCode"], children: [
|
|
1686
1768
|
text(bill.patient.address.postalCode, updatePostalCode, { maxLength: 10 }),
|
|
1687
1769
|
postalStatus ? /* @__PURE__ */ jsx3("small", { className: "mbsf-help", children: postalStatus }) : null
|
|
1688
1770
|
] }),
|
|
1689
|
-
/* @__PURE__ */ jsx3(Field2, { label: "City", required: true, error: errors["patient.address.city"], children: text(bill.patient.address.city, (city) => setAddress({ city })) }),
|
|
1690
|
-
/* @__PURE__ */ jsx3(Field2, { label: "State", required: true, error: errors["patient.address.state"], children: text(bill.patient.address.state, (state) => setAddress({ state: state.toUpperCase() }), { maxLength: 2 }) })
|
|
1771
|
+
/* @__PURE__ */ jsx3(Field2, { path: "patient.address.city", label: "City", required: true, error: errors["patient.address.city"], children: text(bill.patient.address.city, (city) => setAddress({ city })) }),
|
|
1772
|
+
/* @__PURE__ */ jsx3(Field2, { path: "patient.address.state", label: "State", required: true, error: errors["patient.address.state"], children: text(bill.patient.address.state, (state) => setAddress({ state: state.toUpperCase() }), { maxLength: 2 }) })
|
|
1691
1773
|
] })
|
|
1692
1774
|
] }),
|
|
1693
1775
|
/* @__PURE__ */ jsxs3("fieldset", { className: "mbsf-card", disabled: locked, children: [
|
|
1694
1776
|
/* @__PURE__ */ jsx3("legend", { className: "mbsf-legend", children: "Injury & claim" }),
|
|
1695
1777
|
/* @__PURE__ */ jsxs3("div", { className: "mbsf-grid", children: [
|
|
1696
|
-
/* @__PURE__ */ jsx3(Field2, { label: "Date of service", required: true, error: errors["service.date"], children: /* @__PURE__ */ jsx3(TextDateInput, { ariaLabel: "Date of service", value: bill.service.date, disabled: locked, required: true, onChange: (date2) => setBill((c) => ({ ...c, service: { ...c.service, date: date2 } })) }) }),
|
|
1778
|
+
/* @__PURE__ */ jsx3(Field2, { path: "service.date", label: "Date of service", required: true, error: errors["service.date"], children: /* @__PURE__ */ jsx3(TextDateInput, { ariaLabel: "Date of service", value: bill.service.date, disabled: locked, required: true, onChange: (date2) => setBill((c) => ({ ...c, service: { ...c.service, date: date2 } })) }) }),
|
|
1697
1779
|
/* @__PURE__ */ jsx3(Field2, { label: "Date of injury", children: /* @__PURE__ */ jsx3(TextDateInput, { ariaLabel: "Date of injury", value: bill.claim.dateOfInjury, disabled: locked, onChange: (dateOfInjury) => setBill((c) => ({ ...c, claim: { ...c.claim, dateOfInjury } })) }) }),
|
|
1698
1780
|
/* @__PURE__ */ jsx3(Field2, { label: "Employer (optional)", children: text(bill.claim.employer, (employer) => setBill((c) => ({ ...c, claim: { ...c.claim, employer } }))) }),
|
|
1699
|
-
/* @__PURE__ */ jsx3(Field2, { label: "Claim number", required: true, error: errors["claim.claimNumber"], children: text(bill.claim.claimNumber, (claimNumber) => setBill((c) => ({ ...c, claim: { ...c.claim, claimNumber } }))) }),
|
|
1781
|
+
/* @__PURE__ */ jsx3(Field2, { path: "claim.claimNumber", label: "Claim number", required: true, error: errors["claim.claimNumber"], children: text(bill.claim.claimNumber, (claimNumber) => setBill((c) => ({ ...c, claim: { ...c.claim, claimNumber } }))) }),
|
|
1700
1782
|
/* @__PURE__ */ jsx3(Field2, { label: "WCAB / ADJ number (optional)", children: text(bill.claim.adjNumber, (adjNumber) => setBill((c) => ({ ...c, claim: { ...c.claim, adjNumber } }))) }),
|
|
1701
|
-
/* @__PURE__ */ jsx3(Field2, { label: "Claims administrator", required: true, span: true, error:
|
|
1783
|
+
/* @__PURE__ */ jsx3(Field2, { path: "claim.claimsAdministrator", label: "Claims administrator", required: true, span: true, error: claimsAdministratorError, children: /* @__PURE__ */ jsx3(ComboBox, { ariaLabel: "Claims administrator", invalid: Boolean(claimsAdministratorError), disabled: locked, loading: payerLoading, value: bill.claim.claimsAdministrator?.name ?? "", placeholder: "Search the payer directory\u2026", options: payerResults.map((payer) => ({ id: payer.id, label: payer.name, detail: [payer.hasElectronic ? "Electronic" : "Work comp", ...payer.states ?? []].join(" \xB7 ") })), onQuery: searchPayers, onSelect: (option) => setBill((c) => ({ ...c, claim: { ...c.claim, claimsAdministrator: { id: option.id, name: option.label } } })) }) }),
|
|
1702
1784
|
/* @__PURE__ */ jsx3(Field2, { label: "Injury description (optional)", span: true, children: text(bill.claim.description, (description2) => setBill((c) => ({ ...c, claim: { ...c.claim, description: description2 } }))) }),
|
|
1703
1785
|
/* @__PURE__ */ jsxs3(Field2, { label: "Diagnosis codes (ICD-10)", span: true, children: [
|
|
1704
1786
|
/* @__PURE__ */ jsx3("div", { className: "mbsf-quick-picks", "aria-label": "Common diagnosis codes", children: BILL_SUBMISSION_DIAGNOSIS_QUICK_PICKS.map((option) => {
|
|
@@ -1710,14 +1792,16 @@ function BillSubmissionForm({
|
|
|
1710
1792
|
] }, option.code);
|
|
1711
1793
|
}) }),
|
|
1712
1794
|
/* @__PURE__ */ jsx3("div", { className: "mbsf-chips", children: (bill.diagnoses ?? []).map((code) => {
|
|
1713
|
-
const option = [...BILL_SUBMISSION_DIAGNOSIS_QUICK_PICKS, ...
|
|
1795
|
+
const option = [...BILL_SUBMISSION_DIAGNOSIS_QUICK_PICKS, ...diagnosisChoices].find((item) => item.code === code);
|
|
1714
1796
|
return /* @__PURE__ */ jsxs3("span", { className: "mbsf-chip", children: [
|
|
1715
1797
|
/* @__PURE__ */ jsx3("strong", { children: code }),
|
|
1716
1798
|
option?.description ? ` ${option.description}` : "",
|
|
1717
1799
|
/* @__PURE__ */ jsx3("button", { type: "button", "aria-label": `Remove ${code}`, onClick: () => setBill((c) => ({ ...c, diagnoses: (c.diagnoses ?? []).filter((item) => item !== code) })), children: "\xD7" })
|
|
1718
1800
|
] }, code);
|
|
1719
1801
|
}) }),
|
|
1720
|
-
/* @__PURE__ */ jsx3(ComboBox, { ariaLabel: "Add diagnosis code", disabled: locked, loading: diagnosisLoading, value: "", placeholder: bill.diagnoses?.length ?? 0 ? `${bill.diagnoses.length} selected \u2014 add more\u2026` : "Search ICD-10 codes\u2026", options:
|
|
1802
|
+
/* @__PURE__ */ jsx3(ComboBox, { ariaLabel: "Add diagnosis code", disabled: locked, loading: diagnosisLoading, loadingMore: diagnosisLoadingMore, value: "", placeholder: bill.diagnoses?.length ?? 0 ? `${bill.diagnoses.length} selected \u2014 add more\u2026` : "Search ICD-10 codes\u2026", options: diagnosisChoices.filter((item) => !(bill.diagnoses ?? []).includes(item.code)).map((item) => ({ id: item.code, label: item.code, detail: item.description })), onOpen: () => {
|
|
1803
|
+
if (diagnosisQuery !== "") loadDiagnoses("");
|
|
1804
|
+
}, onQuery: searchDiagnoses, onEndReached: () => loadDiagnoses(diagnosisQuery ?? "", true), onSelect: (option) => setBill((c) => ({ ...c, diagnoses: [.../* @__PURE__ */ new Set([...c.diagnoses ?? [], option.id])] })) })
|
|
1721
1805
|
] })
|
|
1722
1806
|
] })
|
|
1723
1807
|
] }),
|
|
@@ -1742,7 +1826,7 @@ function BillSubmissionForm({
|
|
|
1742
1826
|
/* @__PURE__ */ jsx3("p", { className: "mbsf-help", children: "Sets the evaluator/specialty modifier on medical-legal evaluation lines." }),
|
|
1743
1827
|
/* @__PURE__ */ jsx3("div", { className: "mbsf-segments", role: "group", "aria-label": "Evaluation type", children: [["qme", "QME (default)"], ["ame", "AME"], ["psych_qme", "Psych QME"]].map(([type, label]) => /* @__PURE__ */ jsx3("button", { className: "mbsf-segment", type: "button", "aria-pressed": evaluationType === type, onClick: () => changeEvaluation(type), children: label }, type)) }),
|
|
1744
1828
|
/* @__PURE__ */ jsx3("p", { className: "mbsf-help", children: evaluationType === "ame" ? "Agreed Medical Evaluator \u2014 eligible ML evaluation codes default to modifier -94." : evaluationType === "psych_qme" ? "Psychiatric QME \u2014 eligible ML evaluation codes default to modifier -96 (-95 for ML200)." : "Qualified Medical Evaluator \u2014 eligible ML evaluation codes default to modifier -95." }),
|
|
1745
|
-
/* @__PURE__ */ jsxs3("div", { className: "mbsf-lines", children: [
|
|
1829
|
+
/* @__PURE__ */ jsxs3("div", { className: "mbsf-lines", "data-field-path": "serviceLines", "data-invalid": Boolean(errors.serviceLines), children: [
|
|
1746
1830
|
/* @__PURE__ */ jsxs3("div", { className: "mbsf-line-head", children: [
|
|
1747
1831
|
/* @__PURE__ */ jsx3("span", { children: "Procedure code" }),
|
|
1748
1832
|
/* @__PURE__ */ jsx3("span", { children: "Modifiers" }),
|
|
@@ -1752,13 +1836,13 @@ function BillSubmissionForm({
|
|
|
1752
1836
|
" "
|
|
1753
1837
|
] }),
|
|
1754
1838
|
bill.serviceLines.map((line, index) => /* @__PURE__ */ jsxs3("div", { className: "mbsf-line", children: [
|
|
1755
|
-
/* @__PURE__ */ jsxs3("div", { "data-label": "Procedure code", children: [
|
|
1756
|
-
/* @__PURE__ */ jsx3(ComboBox, { ariaLabel: `Procedure code ${index + 1}`, disabled: locked, value: line.code, placeholder: "Search or enter code\u2026", options: procedures.map((item) => ({ id: item.code, label: item.code, detail: item.description })), createOption: customProcedureOption, onSelect: (option) => {
|
|
1839
|
+
/* @__PURE__ */ jsxs3("div", { "data-label": "Procedure code", "data-field-path": `serviceLines.${index}.code`, "data-invalid": Boolean(errors[`serviceLines.${index}.code`]), children: [
|
|
1840
|
+
/* @__PURE__ */ jsx3(ComboBox, { ariaLabel: `Procedure code ${index + 1}`, invalid: Boolean(errors[`serviceLines.${index}.code`]), disabled: locked, value: line.code, placeholder: "Search or enter code\u2026", options: procedures.map((item) => ({ id: item.code, label: item.code, detail: item.description })), createOption: customProcedureOption, onSelect: (option) => {
|
|
1757
1841
|
const auto = evaluationModifier(evaluationType, option.id);
|
|
1758
1842
|
setLine(index, { code: option.id, ...auto ? { modifiers: [auto, ...(line.modifiers ?? []).filter((item) => !["94", "95", "96"].includes(item.replace(/^-/, "")))] } : line.modifiers ? { modifiers: line.modifiers } : {} });
|
|
1759
1843
|
} }),
|
|
1760
1844
|
line.code ? /* @__PURE__ */ jsx3("small", { className: "mbsf-help", children: procedures.find((item) => item.code === line.code)?.description ?? "Custom CPT, HCPCS, or medical-legal code" }) : null,
|
|
1761
|
-
errors[`serviceLines.${index}.code`] ? /* @__PURE__ */ jsx3("small", { className: "mbsf-error", children: errors[`serviceLines.${index}.code`] }) : null
|
|
1845
|
+
errors[`serviceLines.${index}.code`] ? /* @__PURE__ */ jsx3("small", { className: "mbsf-error", role: "alert", children: errors[`serviceLines.${index}.code`] }) : null
|
|
1762
1846
|
] }),
|
|
1763
1847
|
/* @__PURE__ */ jsxs3("div", { "data-label": "Modifiers", children: [
|
|
1764
1848
|
/* @__PURE__ */ jsx3("div", { className: "mbsf-chips", children: (line.modifiers ?? []).map((modifier) => /* @__PURE__ */ jsxs3("span", { className: "mbsf-chip", children: [
|
|
@@ -1768,9 +1852,9 @@ function BillSubmissionForm({
|
|
|
1768
1852
|
] }, modifier)) }),
|
|
1769
1853
|
/* @__PURE__ */ jsx3(ComboBox, { ariaLabel: `Modifiers ${index + 1}`, disabled: locked, value: "", placeholder: line.modifiers?.length ?? 0 ? `${line.modifiers.length} modifier${line.modifiers.length === 1 ? "" : "s"}` : "Add modifiers\u2026", options: modifiers.filter((item) => !(line.modifiers ?? []).includes(item.code)).map((item) => ({ id: item.code, label: `\u2212${item.code}`, detail: item.description })), onSelect: (option) => setLine(index, { modifiers: [.../* @__PURE__ */ new Set([...line.modifiers ?? [], option.id])] }) })
|
|
1770
1854
|
] }),
|
|
1771
|
-
/* @__PURE__ */ jsxs3("div", { "data-label": "Units", children: [
|
|
1772
|
-
/* @__PURE__ */ jsx3("input", { className: "mbsf-input", "aria-label": `Units ${index + 1}`, type: "number", min: 1, value: line.units ?? 1, onChange: (event) => setLine(index, { units: Number(event.target.value) }) }),
|
|
1773
|
-
errors[`serviceLines.${index}.units`] ? /* @__PURE__ */ jsx3("small", { className: "mbsf-error", children: errors[`serviceLines.${index}.units`] }) : null
|
|
1855
|
+
/* @__PURE__ */ jsxs3("div", { "data-label": "Units", "data-field-path": `serviceLines.${index}.units`, "data-invalid": Boolean(errors[`serviceLines.${index}.units`]), children: [
|
|
1856
|
+
/* @__PURE__ */ jsx3("input", { className: "mbsf-input", "aria-label": `Units ${index + 1}`, "aria-invalid": Boolean(errors[`serviceLines.${index}.units`]), type: "number", min: 1, value: line.units ?? 1, onChange: (event) => setLine(index, { units: Number(event.target.value) }) }),
|
|
1857
|
+
errors[`serviceLines.${index}.units`] ? /* @__PURE__ */ jsx3("small", { className: "mbsf-error", role: "alert", children: errors[`serviceLines.${index}.units`] }) : null
|
|
1774
1858
|
] }),
|
|
1775
1859
|
/* @__PURE__ */ jsx3("div", { className: "mbsf-money", "data-label": "Allowed", children: lineCharge(line) == null ? "\u2014" : lineCharge(line).toLocaleString(void 0, { style: "currency", currency: "USD" }) }),
|
|
1776
1860
|
/* @__PURE__ */ jsx3("button", { className: "mbsf-icon-btn", type: "button", "aria-label": `Remove service line ${index + 1}`, disabled: locked || !lineHasContent(line) && index === bill.serviceLines.length - 1, onClick: () => setBill((c) => ({ ...c, serviceLines: ensureTrailingBillSubmissionLine(c.serviceLines.filter((_, itemIndex) => itemIndex !== index)) })), children: "\xD7" })
|
|
@@ -1780,7 +1864,7 @@ function BillSubmissionForm({
|
|
|
1780
1864
|
/* @__PURE__ */ jsx3("span", { children: total.toLocaleString(void 0, { style: "currency", currency: "USD" }) })
|
|
1781
1865
|
] })
|
|
1782
1866
|
] }),
|
|
1783
|
-
errors.serviceLines ? /* @__PURE__ */ jsx3("p", { className: "mbsf-error", children: errors.serviceLines }) : null
|
|
1867
|
+
errors.serviceLines ? /* @__PURE__ */ jsx3("p", { className: "mbsf-error", role: "alert", children: errors.serviceLines }) : null
|
|
1784
1868
|
] }),
|
|
1785
1869
|
/* @__PURE__ */ jsxs3("fieldset", { className: "mbsf-card", disabled: locked, children: [
|
|
1786
1870
|
/* @__PURE__ */ jsx3("legend", { className: "mbsf-legend", children: "Attachments" }),
|