@mindbill/react 0.20.1 → 0.21.1

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 CHANGED
@@ -141,9 +141,6 @@ import { BillSubmissionForm } from "@mindbill/react";
141
141
  initialBill={bootstrap.bill}
142
142
  attachments={bootstrap.attachments}
143
143
  getSession={() => fetch("/api/mindbill/session", { method: "POST" }).then(r => r.json())}
144
- diagnosisOptions={bootstrap.diagnoses}
145
- procedureOptions={bootstrap.procedures}
146
- onLookupPostalCode={lookupPostalCode}
147
144
  appearance={{ preset: "qme-companion" }}
148
145
  onSubmit={({ bill, sourceAttachmentIds, uploads }) =>
149
146
  submitBill({ bill, sourceAttachmentIds, uploads })
@@ -155,14 +152,14 @@ The component includes the interaction model, not just the markup:
155
152
 
156
153
  - a responsive two-column review form (one column on narrow screens);
157
154
  - paste-friendly `MM/DD/YYYY` date fields and required-field asterisks;
158
- - ZIP-to-city/state completion through `onLookupPostalCode`;
159
- - searchable ICD-10 multi-select with removable chips;
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;
160
157
  - canonical claims-administrator search through the authenticated MindBill payer directory;
161
158
  - QME, AME, and Psych QME evaluation modes with medical-legal modifier defaults;
162
- - searchable procedure/modifier controls, fee-schedule amounts, totals, and an automatically maintained empty line;
163
- - auto-attached source documents, practice W-9 treatment, and click, panel-drop, or whole-page PDF upload.
159
+ - searchable workers-comp procedure/modifier controls, medical-legal fee-schedule amounts, totals, valid manual CPT/HCPCS entry, and an automatically maintained empty line;
160
+ - removable source documents with new-tab previews, a locked auto-attached practice W-9, and a full-width click, panel-drop, or whole-page PDF upload area. Every visible source document is included until removed; uploaded PDFs do not require document-type tagging.
164
161
 
165
- Partners supply tenant-specific bootstrap data and server callbacks. Required fields, validation, payer selection, service-line behavior, attachments, and submission UX stay inside `@mindbill/react`, so every integration receives the same billing workflow.
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.
166
163
 
167
164
  `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.
168
165
 
package/dist/index.d.ts CHANGED
@@ -453,6 +453,7 @@ type BillSubmissionSourceAttachment = {
453
453
  fileName: string;
454
454
  documentType: BillSubmissionDocumentType;
455
455
  description?: string;
456
+ /** @deprecated Source attachments shown in the form are included until removed. */
456
457
  selected?: boolean;
457
458
  /** Auto-attached documents such as a practice W-9 cannot be deselected. */
458
459
  autoAttached?: boolean;
@@ -504,6 +505,18 @@ declare function applyBillSubmissionEvaluationModifiers(lines: BillSubmissionInp
504
505
  declare function validateBillSubmission(bill: BillSubmissionInput): BillSubmissionValidation;
505
506
  declare function BillSubmissionForm({ initialBill, attachments, onSubmit, getSession, sessionEndpoint, apiBaseUrl, fetch: fetchOverride, onSearchClaimsAdministrators, diagnosisOptions, onSearchDiagnoses, onLookupPostalCode, procedureOptions, modifierOptions, appearance, className, style, disabled, submitLabel, heading, description, }: BillSubmissionFormProps): ReactElement;
506
507
 
508
+ /** Public-domain and MindBill-owned metadata. CPT descriptions are intentionally limited to the curated catalog. */
509
+ declare const DEFAULT_BILL_SUBMISSION_PROCEDURES: BillSubmissionProcedureOption[];
510
+ declare const DEFAULT_BILL_SUBMISSION_MODIFIERS: BillSubmissionModifierOption[];
511
+ declare const BILL_SUBMISSION_DIAGNOSIS_QUICK_PICKS: Array<BillSubmissionDiagnosisOption & {
512
+ label: string;
513
+ }>;
514
+ declare function calculateBillSubmissionAllowedAmount(line: {
515
+ code: string;
516
+ modifiers?: string[];
517
+ units?: number;
518
+ }, procedures?: BillSubmissionProcedureOption[]): number | undefined;
519
+
507
520
  type BillStatusData = {
508
521
  billId: string;
509
522
  state: string;
@@ -668,4 +681,4 @@ type MindBillWidgetProps = {
668
681
  declare function MindBillBillTimeline(props: MindBillWidgetProps): ReactElement;
669
682
  declare function MindBillBillReview(props: MindBillWidgetProps): ReactElement;
670
683
 
671
- export { BILL_SUBMISSION_DOCUMENT_TYPES, BILL_SUBMISSION_REQUIRED_FIELDS, type BillActivityEvent, BillActivityTimeline, type BillActivityTimelineProps, BillLifecycleActions, type BillLifecycleActionsProps, BillLifecycleProgress, type BillLifecycleProgressProps, type BillLifecycleStage, BillPayerContactCard, type BillPayerContactCardProps, BillPaymentLedger, type BillPaymentLedgerProps, BillReadOnlyForm, type BillReadOnlyFormProps, BillRemittanceCard, type BillRemittanceCardProps, type BillReviewAttachment, type BillReviewBillingProvider, type BillReviewClinician, type BillReviewData, type BillReviewDocumentType, type BillReviewDraft, type BillReviewFeatures, BillReviewForm, type BillReviewFormProps, type BillReviewLineItem, type BillReviewLocation, type BillReviewPayer, type BillReviewSaveInput, BillSnapshotSummary, type BillSnapshotSummaryProps, type BillStatusAction, type BillStatusClient, type BillStatusClientOptions, type BillStatusData, type BillStatusSession, type BillStatusSessionProvider, type BillStatusSessionRequest, BillStatusSummary, type BillStatusSummaryProps, type BillSubmissionAddress, type BillSubmissionDiagnosisOption, type BillSubmissionDocumentType, type BillSubmissionEvaluationType, BillSubmissionForm, type BillSubmissionFormProps, type BillSubmissionFormValue, type BillSubmissionInput, type BillSubmissionModifierOption, type BillSubmissionPostalPlace, type BillSubmissionProcedureOption, type BillSubmissionRoute, type BillSubmissionSourceAttachment, type BillSubmissionUpload, type BillSubmissionValidation, ConnectedBillLifecycle, type ConnectedBillLifecycleProps, ConnectedBillStatus, type ConnectedBillStatusProps, MindBillBillReview, MindBillBillTimeline, type MindBillReactAppearance, type MindBillThemePreset, type MindBillWidgetProps, type UseBillLifecycleOptions, type UseBillLifecycleResult, type UseBillStatusOptions, type UseBillStatusResult, applyBillSubmissionEvaluationModifiers, billActivityEventLabel, billLifecycleStage, buildBillReviewSaveInput, createBillStatusClient, ensureTrailingBillSubmissionLine, ensureTrailingProcedureLine, formatBillSubmissionDate, mindBillAppearanceStyle, mindBillThemePresets, parseBillSubmissionDate, resolveMindBillAppearance, useBillLifecycle, useBillStatus, validateBillSubmission, visibleBillLifecycleActions };
684
+ export { BILL_SUBMISSION_DIAGNOSIS_QUICK_PICKS, BILL_SUBMISSION_DOCUMENT_TYPES, BILL_SUBMISSION_REQUIRED_FIELDS, type BillActivityEvent, BillActivityTimeline, type BillActivityTimelineProps, BillLifecycleActions, type BillLifecycleActionsProps, BillLifecycleProgress, type BillLifecycleProgressProps, type BillLifecycleStage, BillPayerContactCard, type BillPayerContactCardProps, BillPaymentLedger, type BillPaymentLedgerProps, BillReadOnlyForm, type BillReadOnlyFormProps, BillRemittanceCard, type BillRemittanceCardProps, type BillReviewAttachment, type BillReviewBillingProvider, type BillReviewClinician, type BillReviewData, type BillReviewDocumentType, type BillReviewDraft, type BillReviewFeatures, BillReviewForm, type BillReviewFormProps, type BillReviewLineItem, type BillReviewLocation, type BillReviewPayer, type BillReviewSaveInput, BillSnapshotSummary, type BillSnapshotSummaryProps, type BillStatusAction, type BillStatusClient, type BillStatusClientOptions, type BillStatusData, type BillStatusSession, type BillStatusSessionProvider, type BillStatusSessionRequest, BillStatusSummary, type BillStatusSummaryProps, type BillSubmissionAddress, type BillSubmissionDiagnosisOption, type BillSubmissionDocumentType, type BillSubmissionEvaluationType, BillSubmissionForm, type BillSubmissionFormProps, type BillSubmissionFormValue, type BillSubmissionInput, type BillSubmissionModifierOption, type BillSubmissionPostalPlace, type BillSubmissionProcedureOption, type BillSubmissionRoute, type BillSubmissionSourceAttachment, type BillSubmissionUpload, type BillSubmissionValidation, ConnectedBillLifecycle, type ConnectedBillLifecycleProps, ConnectedBillStatus, type ConnectedBillStatusProps, DEFAULT_BILL_SUBMISSION_MODIFIERS, DEFAULT_BILL_SUBMISSION_PROCEDURES, MindBillBillReview, MindBillBillTimeline, type MindBillReactAppearance, type MindBillThemePreset, type MindBillWidgetProps, type UseBillLifecycleOptions, type UseBillLifecycleResult, type UseBillStatusOptions, type UseBillStatusResult, applyBillSubmissionEvaluationModifiers, billActivityEventLabel, billLifecycleStage, buildBillReviewSaveInput, calculateBillSubmissionAllowedAmount, createBillStatusClient, ensureTrailingBillSubmissionLine, ensureTrailingProcedureLine, formatBillSubmissionDate, mindBillAppearanceStyle, mindBillThemePresets, parseBillSubmissionDate, resolveMindBillAppearance, useBillLifecycle, useBillStatus, validateBillSubmission, visibleBillLifecycleActions };
package/dist/index.js CHANGED
@@ -1159,6 +1159,127 @@ import { useCallback, useEffect as useEffect2, useMemo as useMemo2, useRef as us
1159
1159
  import {
1160
1160
  createBillReferenceClient
1161
1161
  } from "@mindbill/browser";
1162
+
1163
+ // src/billing-catalog.ts
1164
+ var DEFAULT_BILL_SUBMISSION_PROCEDURES = [
1165
+ { code: "ML200", description: "Missed appointment for a medical-legal evaluation", allowedAmount: 503.75 },
1166
+ { code: "ML201", description: "Comprehensive medical-legal evaluation", allowedAmount: 2015 },
1167
+ { code: "ML202", description: "Follow-up medical-legal evaluation", allowedAmount: 1316.25 },
1168
+ { code: "ML203", description: "Supplemental medical-legal evaluation", allowedAmount: 650 },
1169
+ { code: "ML204", description: "Medical-legal testimony", allowedAmount: 113.75 },
1170
+ { code: "ML205", description: "Review of sub rosa recordings", allowedAmount: 81.25 },
1171
+ { code: "MLPRR", description: "Medical-legal record review", allowedAmount: 3 },
1172
+ { code: "90791", description: "Psychiatric diagnostic evaluation" },
1173
+ { code: "90792", description: "Psychiatric diagnostic evaluation with medical services" },
1174
+ { code: "90832", description: "Psychotherapy, 30 minutes" },
1175
+ { code: "90834", description: "Psychotherapy, 45 minutes" },
1176
+ { code: "90837", description: "Psychotherapy, 60 minutes" },
1177
+ { code: "90846", description: "Family psychotherapy without patient" },
1178
+ { code: "90847", description: "Family psychotherapy with patient" },
1179
+ { code: "90853", description: "Group psychotherapy" },
1180
+ { code: "90785", description: "Interactive complexity" },
1181
+ { code: "90833", description: "Psychotherapy add-on, 30 minutes" },
1182
+ { code: "90836", description: "Psychotherapy add-on, 45 minutes" },
1183
+ { code: "90838", description: "Psychotherapy add-on, 60 minutes" },
1184
+ { code: "99202", description: "New patient office visit, level 2" },
1185
+ { code: "99203", description: "New patient office visit, level 3" },
1186
+ { code: "99204", description: "New patient office visit, level 4" },
1187
+ { code: "99205", description: "New patient office visit, level 5", allowedAmount: 349.48 },
1188
+ { code: "99211", description: "Established patient office visit, level 1" },
1189
+ { code: "99212", description: "Established patient office visit, level 2" },
1190
+ { code: "99213", description: "Established patient office visit, level 3" },
1191
+ { code: "99214", description: "Established patient office visit, level 4" },
1192
+ { code: "99215", description: "Established patient office visit, level 5" },
1193
+ { code: "96130", description: "Psychological testing evaluation, first hour" },
1194
+ { code: "96131", description: "Psychological testing evaluation, each additional hour", allowedAmount: 131.96 },
1195
+ { code: "96136", description: "Psychological or neuropsychological testing, first 30 minutes" },
1196
+ { code: "96137", description: "Psychological or neuropsychological testing, each additional 30 minutes" },
1197
+ { code: "97161", description: "Physical therapy evaluation, low complexity" },
1198
+ { code: "97162", description: "Physical therapy evaluation, moderate complexity" },
1199
+ { code: "97163", description: "Physical therapy evaluation, high complexity" },
1200
+ { code: "97110", description: "Therapeutic exercises" },
1201
+ { code: "97112", description: "Neuromuscular reeducation" },
1202
+ { code: "97140", description: "Manual therapy techniques" },
1203
+ { code: "97530", description: "Therapeutic activities" },
1204
+ { code: "98940", description: "Chiropractic manipulative treatment, 1-2 regions" },
1205
+ { code: "98941", description: "Chiropractic manipulative treatment, 3-4 regions" },
1206
+ { code: "98942", description: "Chiropractic manipulative treatment, 5 regions" },
1207
+ { code: "72100", description: "Spine radiograph, lumbosacral, 2-3 views" },
1208
+ { code: "73030", description: "Shoulder radiograph, complete" },
1209
+ { code: "72141", description: "MRI cervical spine without contrast" },
1210
+ { code: "72148", description: "MRI lumbar spine without contrast" },
1211
+ { code: "73721", description: "MRI lower-extremity joint without contrast" },
1212
+ { code: "20610", description: "Major joint injection or aspiration" },
1213
+ { code: "20552", description: "Trigger point injection, 1-2 muscles" },
1214
+ { code: "64483", description: "Transforaminal epidural injection, lumbar or sacral" },
1215
+ { code: "64635", description: "Destruction by neurolytic agent, paravertebral facet joint nerve" },
1216
+ { code: "99070", description: "Supplies and materials" },
1217
+ { code: "L0650", description: "Lumbar orthosis" },
1218
+ { code: "A4556", description: "Electrodes" }
1219
+ ];
1220
+ var DEFAULT_BILL_SUBMISSION_MODIFIERS = [
1221
+ { code: "92", description: "Primary Treating Physician evaluation" },
1222
+ { code: "93", description: "Interpreter required" },
1223
+ { code: "94", description: "Agreed Medical Evaluator" },
1224
+ { code: "95", description: "Qualified Medical Evaluator" },
1225
+ { code: "96", description: "Psychiatric/psychological evaluation" },
1226
+ { code: "97", description: "Toxicology evaluation" },
1227
+ { code: "98", description: "Oncology evaluation" },
1228
+ { code: "59", description: "Distinct procedural service" },
1229
+ { code: "XE", description: "Separate encounter" },
1230
+ { code: "XS", description: "Separate structure" },
1231
+ { code: "XP", description: "Separate practitioner" },
1232
+ { code: "XU", description: "Unusual non-overlapping service" }
1233
+ ];
1234
+ var BILL_SUBMISSION_DIAGNOSIS_QUICK_PICKS = [
1235
+ { label: "Back", code: "M54.50", description: "Low back pain, unspecified" },
1236
+ { label: "Neck", code: "M54.2", description: "Cervicalgia" },
1237
+ { label: "Left hand", code: "M79.642", description: "Pain in left hand" },
1238
+ { label: "Right hand", code: "M79.641", description: "Pain in right hand" },
1239
+ { label: "Left knee", code: "M25.562", description: "Pain in left knee" },
1240
+ { label: "Right knee", code: "M25.561", description: "Pain in right knee" }
1241
+ ];
1242
+ var COMPOSITE_MULTIPLIERS = {
1243
+ "93+94+96": 2.45,
1244
+ "93+94+97": 1.95,
1245
+ "93+94+98": 1.95,
1246
+ "92+93": 1.1,
1247
+ "93+95": 1.1,
1248
+ "93+94": 1.45,
1249
+ "93+96": 2.1,
1250
+ "93+97": 1.6,
1251
+ "93+98": 1.6,
1252
+ "94+96": 2.35,
1253
+ "94+97": 1.85,
1254
+ "94+98": 1.85
1255
+ };
1256
+ var MED_LEGAL_MODIFIER_MULTIPLIERS = {
1257
+ "92": 1,
1258
+ "93": 1.1,
1259
+ "94": 1.35,
1260
+ "95": 1,
1261
+ "96": 2,
1262
+ "97": 1.5,
1263
+ "98": 1.5
1264
+ };
1265
+ function medicalLegalMultiplier(modifiers) {
1266
+ const priced = modifiers.filter((item) => MED_LEGAL_MODIFIER_MULTIPLIERS[item] !== 1);
1267
+ if (priced.length === 0) return 1;
1268
+ const composite = COMPOSITE_MULTIPLIERS[priced.join("+")];
1269
+ if (composite != null) return composite;
1270
+ return priced.reduce((total, item) => total * (MED_LEGAL_MODIFIER_MULTIPLIERS[item] ?? 1), 1);
1271
+ }
1272
+ function calculateBillSubmissionAllowedAmount(line, procedures = DEFAULT_BILL_SUBMISSION_PROCEDURES) {
1273
+ const procedure = procedures.find((item) => item.code.toUpperCase() === line.code.trim().toUpperCase());
1274
+ if (procedure?.allowedAmount == null) return void 0;
1275
+ const code = procedure.code.toUpperCase();
1276
+ const units = Math.max(1, line.units ?? 1);
1277
+ const normalized = [...new Set((line.modifiers ?? []).map((item) => item.replace(/^-/, "").toUpperCase()))].sort();
1278
+ const multiplier = ["ML200", "ML201", "ML202", "ML203"].includes(code) ? medicalLegalMultiplier(normalized) : 1;
1279
+ return Math.round(procedure.allowedAmount * units * multiplier * 100) / 100;
1280
+ }
1281
+
1282
+ // src/bill-submission-form.tsx
1162
1283
  import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1163
1284
  var BILL_SUBMISSION_DOCUMENT_TYPES = [
1164
1285
  "final_report",
@@ -1185,25 +1306,6 @@ var BILL_SUBMISSION_REQUIRED_FIELDS = [
1185
1306
  "serviceLines[].code",
1186
1307
  "serviceLines[].units"
1187
1308
  ];
1188
- var PROCEDURES2 = [
1189
- { code: "ML200", description: "Missed appointment for a medical-legal evaluation" },
1190
- { code: "ML201", description: "Comprehensive medical-legal evaluation" },
1191
- { code: "ML202", description: "Follow-up medical-legal evaluation" },
1192
- { code: "ML203", description: "Supplemental medical-legal evaluation" },
1193
- { code: "ML204", description: "Medical-legal testimony" },
1194
- { code: "ML205", description: "Review of sub rosa recordings" },
1195
- { code: "MLPRR", description: "Medical-legal record review" }
1196
- ];
1197
- var MODIFIERS2 = [
1198
- { code: "92", description: "Primary Treating Physician evaluation" },
1199
- { code: "93", description: "Interpreter required" },
1200
- { code: "94", description: "Agreed Medical Evaluator" },
1201
- { code: "95", description: "Qualified Medical Evaluator" },
1202
- { code: "96", description: "Psychiatric/psychological evaluation" },
1203
- { code: "97", description: "Toxicology evaluation" },
1204
- { code: "98", description: "Oncology evaluation" },
1205
- { code: "59", description: "Distinct procedural service" }
1206
- ];
1207
1309
  var documentLabels = {
1208
1310
  final_report: "Final report",
1209
1311
  proof_of_service: "Proof of service",
@@ -1219,6 +1321,11 @@ var MAX_PDF_BYTES = 25 * 1024 * 1024;
1219
1321
  var MAX_UPLOAD_BYTES = 100 * 1024 * 1024;
1220
1322
  var MAX_DOCUMENTS = 25;
1221
1323
  var EMPTY_ATTACHMENTS = [];
1324
+ function previewUploadedPdf(file) {
1325
+ const url = URL.createObjectURL(file);
1326
+ window.open(url, "_blank", "noopener,noreferrer");
1327
+ window.setTimeout(() => URL.revokeObjectURL(url), 6e4);
1328
+ }
1222
1329
  function blankLine() {
1223
1330
  return { code: "", modifiers: [], units: 1 };
1224
1331
  }
@@ -1313,12 +1420,12 @@ var css = `
1313
1420
  .mbsf{display:grid;gap:20px;color:var(--mb-text);font-family:var(--mb-font);font-size:15px}.mbsf *{box-sizing:border-box}
1314
1421
  .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)}
1315
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)}
1316
- .mbsf-combo{position:relative}.mbsf-menu{position:absolute;z-index:20;top:calc(100% + 5px);left:0;right:0;max-height:300px;overflow:auto;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)}
1317
- .mbsf-chips{display:flex;flex-wrap:wrap;gap:7px;margin-bottom:8px}.mbsf-chip{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}
1423
+ .mbsf-combo{position:relative}.mbsf-menu{position:absolute;z-index:20;top:calc(100% + 5px);left:0;right:0;max-height:min(300px,42vh);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)}
1424
+ .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)}
1318
1425
  .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)}
1319
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}
1320
- .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-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;place-items:center;min-height:180px;padding:24px;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));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}
1321
- @media(max-width:820px){.mbsf-grid{grid-template-columns:1fr}.mbsf-span{grid-column:auto}.mbsf-card{padding:18px}.mbsf-line-head{display:none}.mbsf-line{grid-template-columns:1fr 1fr}.mbsf-line>*:nth-child(1),.mbsf-line>*:nth-child(2){grid-column:1/-1}.mbsf-money{text-align:left}.mbsf-line .mbsf-icon-btn{justify-self:end}.mbsf-total{padding-right:18px}.mbsf-head{align-items:flex-start}.mbsf-segments{grid-template-columns:1fr}.mbsf-segment{border-right:0;border-bottom:1px solid var(--mb-border)}.mbsf-segment:last-child{border-bottom:0}}
1427
+ .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
+ @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%}}
1322
1429
  `;
1323
1430
  function RequiredMark() {
1324
1431
  return /* @__PURE__ */ jsx3("span", { className: "mbsf-star", children: " *" });
@@ -1352,24 +1459,26 @@ function TextDateInput({ value, onChange, disabled, required, ariaLabel }) {
1352
1459
  invalid ? /* @__PURE__ */ jsx3("small", { className: "mbsf-error", children: "Use MM/DD/YYYY" }) : null
1353
1460
  ] });
1354
1461
  }
1355
- function ComboBox({ value, placeholder, options, disabled, loading, onQuery, onSelect, ariaLabel }) {
1462
+ function ComboBox({ value, placeholder, options, disabled, loading, onQuery, onSelect, createOption, ariaLabel }) {
1356
1463
  const [open, setOpen] = useState2(false);
1357
1464
  const [query, setQuery] = useState2("");
1358
1465
  const visible = useMemo2(() => {
1359
1466
  const q = query.trim().toLowerCase();
1360
- return q ? options.filter((option) => `${option.id} ${option.label} ${option.detail ?? ""}`.toLowerCase().includes(q)) : options;
1361
- }, [options, query]);
1467
+ const matches = q ? options.filter((option) => `${option.id} ${option.label} ${option.detail ?? ""}`.toLowerCase().includes(q)) : options;
1468
+ const custom = createOption?.(query) ?? null;
1469
+ return custom && !matches.some((option) => option.id.toUpperCase() === custom.id.toUpperCase()) ? [...matches, custom] : matches;
1470
+ }, [createOption, options, query]);
1471
+ const showMenu = open && (loading || visible.length > 0 || query.trim().length >= 2);
1362
1472
  return /* @__PURE__ */ jsxs3("div", { className: "mbsf-combo", children: [
1363
- /* @__PURE__ */ jsx3("input", { className: "mbsf-input", role: "combobox", "aria-label": ariaLabel, "aria-expanded": open, autoComplete: "off", disabled, placeholder, value: open ? query : value, onFocus: () => {
1473
+ /* @__PURE__ */ jsx3("input", { className: "mbsf-input", role: "combobox", "aria-label": ariaLabel, "aria-expanded": showMenu, autoComplete: "off", disabled, placeholder, value: open ? query : value, onFocus: () => {
1364
1474
  setOpen(true);
1365
1475
  setQuery("");
1366
- onQuery?.("");
1367
1476
  }, onChange: (event) => {
1368
1477
  setOpen(true);
1369
1478
  setQuery(event.target.value);
1370
1479
  onQuery?.(event.target.value);
1371
1480
  }, onBlur: () => setTimeout(() => setOpen(false), 120) }),
1372
- open ? /* @__PURE__ */ jsx3("div", { className: "mbsf-menu", role: "listbox", children: loading ? /* @__PURE__ */ jsx3("div", { className: "mbsf-option", children: "Searching\u2026" }) : visible.length ? visible.map((option) => /* @__PURE__ */ jsxs3("button", { className: "mbsf-option", type: "button", role: "option", onMouseDown: (event) => event.preventDefault(), onClick: () => {
1481
+ showMenu ? /* @__PURE__ */ jsx3("div", { className: "mbsf-menu", role: "listbox", children: loading ? /* @__PURE__ */ jsx3("div", { className: "mbsf-option", children: "Searching\u2026" }) : visible.length ? visible.map((option) => /* @__PURE__ */ jsxs3("button", { className: "mbsf-option", type: "button", role: "option", onMouseDown: (event) => event.preventDefault(), onClick: () => {
1373
1482
  onSelect(option);
1374
1483
  setOpen(false);
1375
1484
  setQuery("");
@@ -1379,6 +1488,11 @@ function ComboBox({ value, placeholder, options, disabled, loading, onQuery, onS
1379
1488
  ] }, option.id)) : /* @__PURE__ */ jsx3("div", { className: "mbsf-option", children: "No matches" }) }) : null
1380
1489
  ] });
1381
1490
  }
1491
+ function customProcedureOption(query) {
1492
+ const code = query.trim().toUpperCase().replace(/\s+/g, "");
1493
+ if (!/^(?:\d{5}|[A-Z]\d{4}|ML(?:20[0-5]|PRR))$/.test(code)) return null;
1494
+ return { id: code, label: code, detail: "Use this CPT, HCPCS, or medical-legal code" };
1495
+ }
1382
1496
  function mergeOptions(defaults, supplied) {
1383
1497
  const map = new Map(defaults.map((item) => [item.code.toUpperCase(), item]));
1384
1498
  supplied?.forEach((item) => map.set(item.code.toUpperCase(), item));
@@ -1407,7 +1521,8 @@ function BillSubmissionForm({
1407
1521
  description = "Review the bill details, add attachments, and submit."
1408
1522
  }) {
1409
1523
  const [bill, setBill] = useState2(() => cloneBill(initialBill));
1410
- const [selectedIds, setSelectedIds] = useState2(() => attachments.filter((item) => item.selected !== false || item.autoAttached).map((item) => item.id));
1524
+ const [selectedIds, setSelectedIds] = useState2(() => attachments.map((item) => item.id));
1525
+ const [removedSourceIds, setRemovedSourceIds] = useState2([]);
1411
1526
  const [uploads, setUploads] = useState2([]);
1412
1527
  const [errors, setErrors] = useState2({});
1413
1528
  const [formError, setFormError] = useState2(null);
@@ -1419,14 +1534,15 @@ function BillSubmissionForm({
1419
1534
  const [postalStatus, setPostalStatus] = useState2(null);
1420
1535
  const [dragActive, setDragActive] = useState2(false);
1421
1536
  const fileInput = useRef2(null);
1422
- const procedures = useMemo2(() => mergeOptions(PROCEDURES2, procedureOptions), [procedureOptions]);
1423
- const modifiers = useMemo2(() => mergeOptions(MODIFIERS2, modifierOptions), [modifierOptions]);
1537
+ const procedures = useMemo2(() => mergeOptions(DEFAULT_BILL_SUBMISSION_PROCEDURES, procedureOptions), [procedureOptions]);
1538
+ const modifiers = useMemo2(() => mergeOptions(DEFAULT_BILL_SUBMISSION_MODIFIERS, modifierOptions), [modifierOptions]);
1424
1539
  const [evaluationType, setEvaluationType] = useState2(() => initialBill.renderingProvider?.isAme ? "ame" : initialBill.renderingProvider?.specialty?.toLowerCase().includes("psych") ? "psych_qme" : "qme");
1425
1540
  const referenceClient = useMemo2(() => getSession ? createBillReferenceClient({ getSession, sessionEndpoint, apiBaseUrl, fetch: fetchOverride }) : null, [getSession, sessionEndpoint, apiBaseUrl, fetchOverride]);
1426
1541
  const locked = disabled || submitting;
1427
1542
  useEffect2(() => {
1428
1543
  setBill(cloneBill(initialBill));
1429
- setSelectedIds(attachments.filter((item) => item.selected !== false || item.autoAttached).map((item) => item.id));
1544
+ setSelectedIds(attachments.map((item) => item.id));
1545
+ setRemovedSourceIds([]);
1430
1546
  setUploads([]);
1431
1547
  setErrors({});
1432
1548
  setFormError(null);
@@ -1471,7 +1587,18 @@ function BillSubmissionForm({
1471
1587
  };
1472
1588
  }, [addFiles]);
1473
1589
  const setAddress = (patch) => setBill((current) => ({ ...current, patient: { ...current.patient, address: { ...current.patient.address, ...patch } } }));
1474
- const setLine = (index, patch) => setBill((current) => ({ ...current, serviceLines: ensureTrailingBillSubmissionLine(current.serviceLines.map((line, lineIndex) => lineIndex === index ? { ...line, ...patch } : line)) }));
1590
+ const setLine = (index, patch) => setBill((current) => ({ ...current, serviceLines: ensureTrailingBillSubmissionLine(current.serviceLines.map((line, lineIndex) => {
1591
+ if (lineIndex !== index) return line;
1592
+ const next = { ...line, ...patch };
1593
+ const charge = calculateBillSubmissionAllowedAmount(next, procedures);
1594
+ if (charge != null) return { ...next, charge };
1595
+ if (Object.hasOwn(patch, "code")) {
1596
+ const withoutCharge = { ...next };
1597
+ delete withoutCharge.charge;
1598
+ return withoutCharge;
1599
+ }
1600
+ return next;
1601
+ })) }));
1475
1602
  const text = (value, onChange, options = {}) => /* @__PURE__ */ jsx3("input", { className: "mbsf-input", disabled: locked, value: value ?? "", onChange: (event) => onChange(event.target.value), ...options });
1476
1603
  const searchPayers = (query) => {
1477
1604
  if (query.trim().length < 2) return;
@@ -1484,15 +1611,23 @@ function BillSubmissionForm({
1484
1611
  void search(query, bill.claim.claimNumber).then(setPayerResults).catch((caught) => setFormError(caught instanceof Error ? caught.message : "Claims administrator search is unavailable.")).finally(() => setPayerLoading(false));
1485
1612
  };
1486
1613
  const searchDiagnoses = (query) => {
1487
- if (!onSearchDiagnoses) return;
1614
+ const trimmed = query.trim();
1615
+ if (!trimmed) {
1616
+ setDiagnosisResults(diagnosisOptions);
1617
+ return;
1618
+ }
1619
+ const search = onSearchDiagnoses ?? referenceClient?.searchDiagnosisCodes;
1620
+ if (!search) return;
1488
1621
  setDiagnosisLoading(true);
1489
- void onSearchDiagnoses(query).then(setDiagnosisResults).catch(() => setDiagnosisResults([])).finally(() => setDiagnosisLoading(false));
1622
+ void search(trimmed).then(setDiagnosisResults).catch(() => setDiagnosisResults([])).finally(() => setDiagnosisLoading(false));
1490
1623
  };
1491
1624
  const updatePostalCode = (postalCode) => {
1492
1625
  setAddress({ postalCode });
1493
1626
  setPostalStatus(null);
1494
- if (!onLookupPostalCode || !/^\d{5}$/.test(postalCode)) return;
1495
- void onLookupPostalCode(postalCode).then((place) => {
1627
+ const lookup = onLookupPostalCode ?? referenceClient?.lookupPostalCode;
1628
+ if (!lookup || !/^\d{5}$/.test(postalCode)) return;
1629
+ setPostalStatus("Looking up ZIP\u2026");
1630
+ void lookup(postalCode).then((place) => {
1496
1631
  if (!place) return setPostalStatus("ZIP not found");
1497
1632
  setAddress({ city: place.city, state: place.state.toUpperCase() });
1498
1633
  setPostalStatus(`${place.city}, ${place.state.toUpperCase()} filled from ZIP`);
@@ -1502,9 +1637,13 @@ function BillSubmissionForm({
1502
1637
  setEvaluationType(type);
1503
1638
  setBill((current) => ({ ...current, renderingProvider: { ...current.renderingProvider, isAme: type === "ame", isQme: type !== "ame", ...type === "psych_qme" && !current.renderingProvider?.specialty ? { specialty: "Psychiatry" } : {} }, serviceLines: applyBillSubmissionEvaluationModifiers(current.serviceLines, type) }));
1504
1639
  };
1505
- const total = submittedLines(bill.serviceLines).reduce((sum, line) => sum + (Number.isFinite(line.charge) ? Number(line.charge) : 0), 0);
1640
+ const lineCharge = (line) => calculateBillSubmissionAllowedAmount(line, procedures) ?? (Number.isFinite(line.charge) ? Number(line.charge) : void 0);
1641
+ const total = submittedLines(bill.serviceLines).reduce((sum, line) => sum + (lineCharge(line) ?? 0), 0);
1506
1642
  async function submit() {
1507
- const clean = { ...cloneBill(bill), serviceLines: submittedLines(bill.serviceLines) };
1643
+ const clean = { ...cloneBill(bill), serviceLines: submittedLines(bill.serviceLines).map((line) => {
1644
+ const charge = lineCharge(line);
1645
+ return charge == null ? line : { ...line, charge };
1646
+ }) };
1508
1647
  const validation = validateBillSubmission(clean);
1509
1648
  setErrors(validation.fieldErrors);
1510
1649
  setFormError(null);
@@ -1562,15 +1701,23 @@ function BillSubmissionForm({
1562
1701
  /* @__PURE__ */ jsx3(Field2, { label: "Claims administrator", required: true, span: true, error: errors["claim.claimsAdministrator"], children: /* @__PURE__ */ jsx3(ComboBox, { ariaLabel: "Claims administrator", 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 } } })) }) }),
1563
1702
  /* @__PURE__ */ jsx3(Field2, { label: "Injury description (optional)", span: true, children: text(bill.claim.description, (description2) => setBill((c) => ({ ...c, claim: { ...c.claim, description: description2 } }))) }),
1564
1703
  /* @__PURE__ */ jsxs3(Field2, { label: "Diagnosis codes (ICD-10)", span: true, children: [
1704
+ /* @__PURE__ */ jsx3("div", { className: "mbsf-quick-picks", "aria-label": "Common diagnosis codes", children: BILL_SUBMISSION_DIAGNOSIS_QUICK_PICKS.map((option) => {
1705
+ const selected = (bill.diagnoses ?? []).includes(option.code);
1706
+ return /* @__PURE__ */ jsxs3("button", { className: "mbsf-quick-pick", "data-selected": selected, type: "button", "aria-pressed": selected, title: `${option.code} \u2014 ${option.description}`, onClick: () => setBill((current) => ({ ...current, diagnoses: selected ? (current.diagnoses ?? []).filter((code) => code !== option.code) : [.../* @__PURE__ */ new Set([...current.diagnoses ?? [], option.code])] })), children: [
1707
+ selected ? "\u2713" : "+",
1708
+ " ",
1709
+ option.label
1710
+ ] }, option.code);
1711
+ }) }),
1565
1712
  /* @__PURE__ */ jsx3("div", { className: "mbsf-chips", children: (bill.diagnoses ?? []).map((code) => {
1566
- const option = [...diagnosisOptions, ...diagnosisResults].find((item) => item.code === code);
1713
+ const option = [...BILL_SUBMISSION_DIAGNOSIS_QUICK_PICKS, ...diagnosisOptions, ...diagnosisResults].find((item) => item.code === code);
1567
1714
  return /* @__PURE__ */ jsxs3("span", { className: "mbsf-chip", children: [
1568
1715
  /* @__PURE__ */ jsx3("strong", { children: code }),
1569
1716
  option?.description ? ` ${option.description}` : "",
1570
1717
  /* @__PURE__ */ jsx3("button", { type: "button", "aria-label": `Remove ${code}`, onClick: () => setBill((c) => ({ ...c, diagnoses: (c.diagnoses ?? []).filter((item) => item !== code) })), children: "\xD7" })
1571
1718
  ] }, code);
1572
1719
  }) }),
1573
- /* @__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: [...new Map([...diagnosisOptions, ...diagnosisResults].map((item) => [item.code, item])).values()].filter((item) => !(bill.diagnoses ?? []).includes(item.code)).map((item) => ({ id: item.code, label: item.code, detail: item.description })), onQuery: searchDiagnoses, onSelect: (option) => setBill((c) => ({ ...c, diagnoses: [.../* @__PURE__ */ new Set([...c.diagnoses ?? [], option.id])] })) })
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: [...new Map([...BILL_SUBMISSION_DIAGNOSIS_QUICK_PICKS, ...diagnosisOptions, ...diagnosisResults].map((item) => [item.code, item])).values()].filter((item) => !(bill.diagnoses ?? []).includes(item.code)).map((item) => ({ id: item.code, label: item.code, detail: item.description })), onQuery: searchDiagnoses, onSelect: (option) => setBill((c) => ({ ...c, diagnoses: [.../* @__PURE__ */ new Set([...c.diagnoses ?? [], option.id])] })) })
1574
1721
  ] })
1575
1722
  ] })
1576
1723
  ] }),
@@ -1605,16 +1752,15 @@ function BillSubmissionForm({
1605
1752
  " "
1606
1753
  ] }),
1607
1754
  bill.serviceLines.map((line, index) => /* @__PURE__ */ jsxs3("div", { className: "mbsf-line", children: [
1608
- /* @__PURE__ */ jsxs3("div", { children: [
1609
- /* @__PURE__ */ jsx3(ComboBox, { ariaLabel: `Procedure code ${index + 1}`, disabled: locked, value: line.code, placeholder: "Code\u2026", options: procedures.map((item) => ({ id: item.code, label: item.code, detail: item.description })), onSelect: (option) => {
1610
- const selected = procedures.find((item) => item.code === option.id);
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) => {
1611
1757
  const auto = evaluationModifier(evaluationType, option.id);
1612
- setLine(index, { code: option.id, ...auto ? { modifiers: [auto, ...(line.modifiers ?? []).filter((item) => !["94", "95", "96"].includes(item.replace(/^-/, "")))] } : line.modifiers ? { modifiers: line.modifiers } : {}, ...selected?.allowedAmount != null ? { charge: selected.allowedAmount * (line.units ?? 1) } : {} });
1758
+ setLine(index, { code: option.id, ...auto ? { modifiers: [auto, ...(line.modifiers ?? []).filter((item) => !["94", "95", "96"].includes(item.replace(/^-/, "")))] } : line.modifiers ? { modifiers: line.modifiers } : {} });
1613
1759
  } }),
1614
- line.code ? /* @__PURE__ */ jsx3("small", { className: "mbsf-help", children: procedures.find((item) => item.code === line.code)?.description }) : null,
1760
+ 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,
1615
1761
  errors[`serviceLines.${index}.code`] ? /* @__PURE__ */ jsx3("small", { className: "mbsf-error", children: errors[`serviceLines.${index}.code`] }) : null
1616
1762
  ] }),
1617
- /* @__PURE__ */ jsxs3("div", { children: [
1763
+ /* @__PURE__ */ jsxs3("div", { "data-label": "Modifiers", children: [
1618
1764
  /* @__PURE__ */ jsx3("div", { className: "mbsf-chips", children: (line.modifiers ?? []).map((modifier) => /* @__PURE__ */ jsxs3("span", { className: "mbsf-chip", children: [
1619
1765
  "\u2212",
1620
1766
  modifier.replace(/^-/, ""),
@@ -1622,15 +1768,11 @@ function BillSubmissionForm({
1622
1768
  ] }, modifier)) }),
1623
1769
  /* @__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])] }) })
1624
1770
  ] }),
1625
- /* @__PURE__ */ jsxs3("div", { children: [
1626
- /* @__PURE__ */ jsx3("input", { className: "mbsf-input", "aria-label": `Units ${index + 1}`, type: "number", min: 1, value: line.units ?? 1, onChange: (event) => {
1627
- const units = Number(event.target.value);
1628
- const selected = procedures.find((item) => item.code === line.code);
1629
- setLine(index, { units, ...selected?.allowedAmount != null ? { charge: selected.allowedAmount * units } : {} });
1630
- } }),
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) }) }),
1631
1773
  errors[`serviceLines.${index}.units`] ? /* @__PURE__ */ jsx3("small", { className: "mbsf-error", children: errors[`serviceLines.${index}.units`] }) : null
1632
1774
  ] }),
1633
- /* @__PURE__ */ jsx3("div", { className: "mbsf-money", children: line.charge == null ? "\u2014" : line.charge.toLocaleString(void 0, { style: "currency", currency: "USD" }) }),
1775
+ /* @__PURE__ */ jsx3("div", { className: "mbsf-money", "data-label": "Allowed", children: lineCharge(line) == null ? "\u2014" : lineCharge(line).toLocaleString(void 0, { style: "currency", currency: "USD" }) }),
1634
1776
  /* @__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" })
1635
1777
  ] }, index)),
1636
1778
  /* @__PURE__ */ jsxs3("div", { className: "mbsf-total", children: [
@@ -1643,31 +1785,39 @@ function BillSubmissionForm({
1643
1785
  /* @__PURE__ */ jsxs3("fieldset", { className: "mbsf-card", disabled: locked, children: [
1644
1786
  /* @__PURE__ */ jsx3("legend", { className: "mbsf-legend", children: "Attachments" }),
1645
1787
  /* @__PURE__ */ jsxs3("div", { className: "mbsf-attach-list", children: [
1646
- attachments.map((attachment) => {
1647
- const auto = attachment.autoAttached ?? attachment.documentType === "w9";
1648
- const selected = selectedIds.includes(attachment.id);
1788
+ attachments.filter((attachment) => !removedSourceIds.includes(attachment.id)).map((attachment) => {
1789
+ const auto = attachment.autoAttached || attachment.documentType === "w9";
1790
+ const removable = !auto && attachment.removable !== false;
1649
1791
  return /* @__PURE__ */ jsxs3("div", { className: "mbsf-attach-row", "data-auto": auto, children: [
1650
- /* @__PURE__ */ jsxs3("label", { className: "mbsf-attach-row", style: { border: 0, padding: 0, flex: 1, justifyContent: "flex-start" }, children: [
1651
- /* @__PURE__ */ jsx3("input", { type: "checkbox", checked: selected, disabled: locked || auto || attachment.removable === false, onChange: (event) => setSelectedIds((current) => event.target.checked ? [.../* @__PURE__ */ new Set([...current, attachment.id])] : current.filter((id) => id !== attachment.id)) }),
1792
+ /* @__PURE__ */ jsxs3("div", { className: "mbsf-attach-main", children: [
1793
+ auto ? /* @__PURE__ */ jsx3("span", { "aria-label": "Always attached", role: "img", children: "\u2713" }) : null,
1652
1794
  /* @__PURE__ */ jsxs3("span", { className: "mbsf-file", children: [
1653
1795
  /* @__PURE__ */ jsx3("strong", { children: attachment.fileName }),
1654
1796
  /* @__PURE__ */ jsx3("span", { className: "mbsf-badge", children: auto ? "Auto-attached" : documentLabels[attachment.documentType] }),
1655
- /* @__PURE__ */ jsx3("span", { className: "mbsf-help", style: { display: "block" }, children: attachment.description || (auto ? "Included automatically with this bill." : documentLabels[attachment.documentType]) })
1797
+ /* @__PURE__ */ jsx3("span", { className: "mbsf-help", style: { display: "block" }, children: attachment.description || (auto ? "Included automatically with every bill." : documentLabels[attachment.documentType]) })
1656
1798
  ] })
1657
1799
  ] }),
1658
- attachment.previewUrl ? /* @__PURE__ */ jsx3("a", { className: "mbsf-secondary", href: attachment.previewUrl, target: "_blank", rel: "noreferrer", children: "View" }) : null
1800
+ /* @__PURE__ */ jsxs3("div", { className: "mbsf-attach-actions", children: [
1801
+ attachment.previewUrl ? /* @__PURE__ */ jsx3("a", { className: "mbsf-secondary", href: attachment.previewUrl, target: "_blank", rel: "noopener noreferrer", children: "Preview" }) : null,
1802
+ removable ? /* @__PURE__ */ jsx3("button", { className: "mbsf-icon-btn", type: "button", "aria-label": `Remove ${attachment.fileName}`, disabled: locked, onClick: () => {
1803
+ setSelectedIds((current) => current.filter((id) => id !== attachment.id));
1804
+ setRemovedSourceIds((current) => [.../* @__PURE__ */ new Set([...current, attachment.id])]);
1805
+ }, children: "\xD7" }) : null
1806
+ ] })
1659
1807
  ] }, attachment.id);
1660
1808
  }),
1661
1809
  uploads.map((upload, index) => /* @__PURE__ */ jsxs3("div", { className: "mbsf-attach-row", children: [
1662
- /* @__PURE__ */ jsxs3("span", { className: "mbsf-file", children: [
1810
+ /* @__PURE__ */ jsx3("div", { className: "mbsf-attach-main", children: /* @__PURE__ */ jsxs3("span", { className: "mbsf-file", children: [
1663
1811
  /* @__PURE__ */ jsx3("strong", { children: upload.file.name }),
1664
1812
  /* @__PURE__ */ jsxs3("span", { className: "mbsf-help", style: { display: "block" }, children: [
1665
1813
  (upload.file.size / 1024 / 1024).toFixed(1),
1666
1814
  " MB"
1667
1815
  ] })
1668
- ] }),
1669
- /* @__PURE__ */ jsx3("select", { className: "mbsf-select", style: { width: 190 }, value: upload.documentType, onChange: (event) => setUploads((current) => current.map((item, itemIndex) => itemIndex === index ? { ...item, documentType: event.target.value } : item)), children: BILL_SUBMISSION_DOCUMENT_TYPES.map((type) => /* @__PURE__ */ jsx3("option", { value: type, children: documentLabels[type] }, type)) }),
1670
- /* @__PURE__ */ jsx3("button", { className: "mbsf-icon-btn", type: "button", "aria-label": `Remove ${upload.file.name}`, onClick: () => setUploads((current) => current.filter((_, itemIndex) => itemIndex !== index)), children: "\xD7" })
1816
+ ] }) }),
1817
+ /* @__PURE__ */ jsxs3("div", { className: "mbsf-attach-actions", children: [
1818
+ /* @__PURE__ */ jsx3("button", { className: "mbsf-secondary", type: "button", onClick: () => previewUploadedPdf(upload.file), children: "Preview" }),
1819
+ /* @__PURE__ */ jsx3("button", { className: "mbsf-icon-btn", type: "button", "aria-label": `Remove ${upload.file.name}`, onClick: () => setUploads((current) => current.filter((_, itemIndex) => itemIndex !== index)), children: "\xD7" })
1820
+ ] })
1671
1821
  ] }, `${upload.file.name}-${index}`))
1672
1822
  ] }),
1673
1823
  /* @__PURE__ */ jsx3("input", { ref: fileInput, hidden: true, type: "file", accept: "application/pdf,.pdf", multiple: true, onChange: (event) => {
@@ -2581,6 +2731,7 @@ function MindBillBillReview(props) {
2581
2731
  return widget("mindbill-bill-review", props);
2582
2732
  }
2583
2733
  export {
2734
+ BILL_SUBMISSION_DIAGNOSIS_QUICK_PICKS,
2584
2735
  BILL_SUBMISSION_DOCUMENT_TYPES,
2585
2736
  BILL_SUBMISSION_REQUIRED_FIELDS,
2586
2737
  BillActivityTimeline,
@@ -2596,12 +2747,15 @@ export {
2596
2747
  BillSubmissionForm,
2597
2748
  ConnectedBillLifecycle,
2598
2749
  ConnectedBillStatus,
2750
+ DEFAULT_BILL_SUBMISSION_MODIFIERS,
2751
+ DEFAULT_BILL_SUBMISSION_PROCEDURES,
2599
2752
  MindBillBillReview,
2600
2753
  MindBillBillTimeline,
2601
2754
  applyBillSubmissionEvaluationModifiers,
2602
2755
  billActivityEventLabel,
2603
2756
  billLifecycleStage,
2604
2757
  buildBillReviewSaveInput,
2758
+ calculateBillSubmissionAllowedAmount,
2605
2759
  createBillLifecycleClient2 as createBillLifecycleClient,
2606
2760
  createBillStatusClient,
2607
2761
  ensureTrailingBillSubmissionLine,