@mindbill/react 0.52.1 → 0.53.0
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 +9 -0
- package/dist/index.d.ts +90 -1
- package/dist/index.js +408 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -389,3 +389,12 @@ import { OrganizationOnboarding } from "@mindbill/react";
|
|
|
389
389
|
```
|
|
390
390
|
|
|
391
391
|
The session must be minted with the optional `organization:manage` permission. Each step saves independently through idempotent upserts that never delete records created elsewhere; the review step mirrors MindBill's onboarding checklist and `onCompleted` fires when everything required is in place.
|
|
392
|
+
|
|
393
|
+
## Dental and authorization drafts
|
|
394
|
+
|
|
395
|
+
`DentalDraftEditor` captures dental services, tooth details, and nullable extended
|
|
396
|
+
practice charges. `RfaDraftForm` prepares unsigned requests for authorization,
|
|
397
|
+
including review type, rationale, and requested services. Both save through your
|
|
398
|
+
host-server callback; saving does not transmit or authorize anything. See
|
|
399
|
+
[treatment draft integration](../../docs/treatment-drafts.md) for input types,
|
|
400
|
+
revision handling, charge semantics, and signing boundaries.
|
package/dist/index.d.ts
CHANGED
|
@@ -1633,6 +1633,95 @@ type ConnectedBillingWorkspaceProps = ConnectedSurfaceProps & {
|
|
|
1633
1633
|
};
|
|
1634
1634
|
declare function ConnectedBillingWorkspace({ appearance, className, style, initialView, onCreateBill, onPostPayment, sandboxControls, getCourtesyCopyRecipientOptions, ...options }: ConnectedBillingWorkspaceProps): ReactElement;
|
|
1635
1635
|
|
|
1636
|
+
type TreatmentDraftAppearance = {
|
|
1637
|
+
appearance?: MindBillReactAppearance;
|
|
1638
|
+
className?: string;
|
|
1639
|
+
style?: CSSProperties;
|
|
1640
|
+
disabled?: boolean;
|
|
1641
|
+
};
|
|
1642
|
+
|
|
1643
|
+
type DentalDraftLineInput = {
|
|
1644
|
+
code: string | null;
|
|
1645
|
+
description: string | null;
|
|
1646
|
+
editionYear: number | null;
|
|
1647
|
+
serviceDate: string | null;
|
|
1648
|
+
quantity: number; /** Extended charge, already includes quantity. Null means unknown. */
|
|
1649
|
+
chargeCents: number | null;
|
|
1650
|
+
chargeReference: string | null;
|
|
1651
|
+
teeth: string[];
|
|
1652
|
+
surfaces: string[];
|
|
1653
|
+
oralCavity: string | null;
|
|
1654
|
+
prosthesisNotes: string | null;
|
|
1655
|
+
};
|
|
1656
|
+
type DentalDraftContentInput = {
|
|
1657
|
+
renderingProviderId: string;
|
|
1658
|
+
billingProviderId?: string | null;
|
|
1659
|
+
diagnosisCodes: string[];
|
|
1660
|
+
notes: string | null;
|
|
1661
|
+
lines: DentalDraftLineInput[];
|
|
1662
|
+
};
|
|
1663
|
+
type DentalDraftEditorProps = TreatmentDraftAppearance & {
|
|
1664
|
+
/** Use a different React key when switching drafts; edits remain local until Save. */
|
|
1665
|
+
initialContent: DentalDraftContentInput;
|
|
1666
|
+
/** Host persists content using the public dental-drafts API and handles revision conflicts. Never transmits a claim. */
|
|
1667
|
+
onSave: (content: DentalDraftContentInput) => Promise<void>;
|
|
1668
|
+
};
|
|
1669
|
+
declare function dentalDraftChargeSummary(lines: readonly Pick<DentalDraftLineInput, "chargeCents">[]): {
|
|
1670
|
+
knownChargeCents: number;
|
|
1671
|
+
totalChargeCents: number | null;
|
|
1672
|
+
};
|
|
1673
|
+
/** Convert decimal dollars exactly; reject excess precision, zero, and exponential notation. */
|
|
1674
|
+
declare function parseDentalCharge(value: string): number | null;
|
|
1675
|
+
declare function validateDentalDraftContent(content: DentalDraftContentInput): string | null;
|
|
1676
|
+
declare function DentalDraftEditor({ initialContent, onSave, disabled, ...appearance }: DentalDraftEditorProps): ReactElement;
|
|
1677
|
+
|
|
1678
|
+
type RfaDraftItemInput = {
|
|
1679
|
+
externalId?: string;
|
|
1680
|
+
diagnosisCode: string;
|
|
1681
|
+
serviceDescription: string;
|
|
1682
|
+
procedureCode?: string;
|
|
1683
|
+
quantity?: number;
|
|
1684
|
+
units?: number;
|
|
1685
|
+
frequency?: string;
|
|
1686
|
+
duration?: string;
|
|
1687
|
+
requestedFrom?: string;
|
|
1688
|
+
requestedTo?: string;
|
|
1689
|
+
metadata?: Record<string, unknown>;
|
|
1690
|
+
};
|
|
1691
|
+
/** An unsigned preparation draft. The host owns identity selection, signing, and delivery. */
|
|
1692
|
+
type RfaDraftInput = {
|
|
1693
|
+
claimId: string;
|
|
1694
|
+
patientId: string;
|
|
1695
|
+
renderingProviderId: string;
|
|
1696
|
+
employeeName: string;
|
|
1697
|
+
providerName: string;
|
|
1698
|
+
externalId?: string;
|
|
1699
|
+
claimsAdminId?: string;
|
|
1700
|
+
requestType?: "new" | "resubmission_material_change" | "oral_authorization_confirmation";
|
|
1701
|
+
reviewType?: "prospective" | "concurrent" | "retrospective";
|
|
1702
|
+
expedited?: boolean;
|
|
1703
|
+
placeOfServiceCode?: string;
|
|
1704
|
+
providerNpi?: string;
|
|
1705
|
+
providerPhone?: string;
|
|
1706
|
+
providerFax?: string;
|
|
1707
|
+
claimNumber?: string;
|
|
1708
|
+
dateOfInjury?: string;
|
|
1709
|
+
rationale?: string;
|
|
1710
|
+
materialChange?: string;
|
|
1711
|
+
items: RfaDraftItemInput[];
|
|
1712
|
+
metadata?: Record<string, unknown>;
|
|
1713
|
+
};
|
|
1714
|
+
type RfaDraftFormProps = TreatmentDraftAppearance & {
|
|
1715
|
+
/** Remount with a different React key when switching requests. Never pass a signed request. */
|
|
1716
|
+
initialDraft: RfaDraftInput;
|
|
1717
|
+
/** Persist an unsigned draft only; this callback must not sign or transmit the request. */
|
|
1718
|
+
onSave: (draft: RfaDraftInput) => Promise<void>;
|
|
1719
|
+
};
|
|
1720
|
+
/** Remove blank optional item fields, retaining an explicitly cleared return fax. */
|
|
1721
|
+
declare function normalizeRfaDraft(draft: RfaDraftInput): RfaDraftInput;
|
|
1722
|
+
declare function validateRfaDraft(draft: RfaDraftInput): string | null;
|
|
1723
|
+
declare function RfaDraftForm({ initialDraft, onSave, disabled, ...appearance }: RfaDraftFormProps): ReactElement;
|
|
1724
|
+
|
|
1636
1725
|
type MindBillWidgetProps = {
|
|
1637
1726
|
sessionToken: string;
|
|
1638
1727
|
embedUrl: string;
|
|
@@ -1645,4 +1734,4 @@ type MindBillWidgetProps = {
|
|
|
1645
1734
|
declare function MindBillBillTimeline(props: MindBillWidgetProps): ReactElement;
|
|
1646
1735
|
declare function MindBillBillReview(props: MindBillWidgetProps): ReactElement;
|
|
1647
1736
|
|
|
1648
|
-
export { BILL_STATUS_AGING_BUCKETS, BILL_SUBMISSION_DIAGNOSIS_QUICK_PICKS, BILL_SUBMISSION_DOCUMENT_TYPES, BILL_SUBMISSION_REPORT_TYPES, BILL_SUBMISSION_REQUIRED_FIELDS, type BillActivityEvent, BillActivityTimeline, type BillActivityTimelineProps, type BillAgingBucket, type BillAgingBucketId, BillAgingSummary, type BillAgingSummaryProps, BillCourtesyCopyForm, type BillCourtesyCopyFormProps, BillExplanationOfReview, type BillExplanationOfReviewProps, BillHistoryTable, type BillHistoryTableProps, BillLifecycleActions, type BillLifecycleActionsProps, BillLifecycleProgress, type BillLifecycleProgressProps, type BillLifecycleProgressStepsOptions, type BillLifecycleStage, BillList, type BillListProps, BillPayerContactCard, type BillPayerContactCardProps, BillPaymentLedger, type BillPaymentLedgerProps, BillReadOnlyForm, type BillReadOnlyFormProps, type BillRegistryAge, type BillRegistryItem, type BillRegistryQuery, type BillRegistryResult, type BillRegistrySort, type BillRegistryStatus, BillRejectionNotice, type BillRejectionNoticeProps, 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 BillStatusAgingCell, BillStatusAgingMatrix, type BillStatusAgingMatrixData, type BillStatusAgingMatrixProps, type BillStatusAgingRow, type BillStatusClient, type BillStatusClientOptions, type BillStatusData, type BillStatusSession, type BillStatusSessionProvider, type BillStatusSessionRequest, BillStatusSummary, type BillStatusSummaryProps, BillSubmissionActions, type BillSubmissionAddress, type BillSubmissionAttachmentReportTypeMode, BillSubmissionAttachmentsSection, BillSubmissionClaimSection, type BillSubmissionDiagnosisOption, type BillSubmissionDocumentType, type BillSubmissionEvaluationType, BillSubmissionForm, type BillSubmissionFormProps, type BillSubmissionFormValue, BillSubmissionHeader, type BillSubmissionInput$1 as BillSubmissionInput, type BillSubmissionModifierOption, BillSubmissionPatientSection, type BillSubmissionPostalPlace, type BillSubmissionProcedureOption, type BillSubmissionProfileOption, type BillSubmissionProfileOptions, BillSubmissionProvidersSection, type BillSubmissionReportTypeOption, type BillSubmissionRibbonItem, type BillSubmissionRoute, type BillSubmissionSectionId, BillSubmissionServiceLinesSection, type BillSubmissionSourceAttachment, type BillSubmissionTaxonomyOption, type BillSubmissionUpload, type BillSubmissionValidation, BillSubmissionsRibbon, type BillSubmissionsRibbonProps, BillTasksDashboard, type BillTasksDashboardCell, type BillTasksDashboardProps, type BillTasksResult, type BillingComponentProps, BillingDashboard, type BillingDashboardBill, type BillingDashboardProps, type BillingDashboardSummary, type BillingOperationsClient, type BillingOperationsClientOptions, BillingReport, type BillingReportDimension, type BillingReportProps, type BillingReportRow, BillingSettings, ClaimsAdministratorDirectoryDialog, type ClaimsAdministratorDirectoryDialogProps, ConnectedBillLifecycle, type ConnectedBillLifecycleProps, ConnectedBillSearch, type ConnectedBillSearchProps, ConnectedBillStatus, type ConnectedBillStatusProps, ConnectedBillTasksDashboard, type ConnectedBillTasksDashboardProps, ConnectedBillingWorkspace, type ConnectedBillingWorkspaceProps, ConnectedNotificationRecipientsSettings, ConnectedNotificationSettings, ConnectedPaymentReview, type ConnectedPaymentReviewProps, ConnectedProductivityReport, ConnectedServiceLineItemsReport, type CourtesyCopyRecipientOption, DEFAULT_BILL_SUBMISSION_MODIFIERS, DEFAULT_BILL_SUBMISSION_PROCEDURES, DEFAULT_BILL_SUBMISSION_TAXONOMIES, MED_LEGAL_REPORT_TYPE_CODE, MindBillBillReview, MindBillBillTimeline, type MindBillReactAppearance, type MindBillThemePreset, type MindBillWidgetProps, type NotificationAgingDay, type NotificationPreferences, type NotificationRecipient, type NotificationRecipientInvitation, type NotificationRecipientsAdapter, NotificationRecipientsSettings, type NotificationRecipientsSettingsProps, type NotificationRecipientsSnapshot, type NotificationReportDigest, NotificationSettings, type NotificationSettingsAdapter, type NotificationSettingsProps, type NotificationSettingsSnapshot, type NotificationSettingsUpdate, OrganizationOnboarding, type OrganizationOnboardingProps, PSYCH_QME_DEFAULT_DIAGNOSIS, type PaymentReviewItem, type PaymentReviewQuery, type PaymentReviewRange, type PaymentReviewResult, type ProductivityReport, ReportBillStatusDialog, type ReportBillStatusDialogProps, SECOND_REVIEW_REASON_TEMPLATES, SecondReviewForm, type SecondReviewFormProps, SendRouteDialog, type SendRouteDialogProps, type SendRouteSubmission, type ServiceLineItemsReport, type UseBillLifecycleOptions, type UseBillLifecycleResult, type UseBillStatusOptions, type UseBillStatusResult, type W9ExtractionStatus, W9Upload, type W9UploadProps, applyBillSubmissionEvaluationDiagnoses, applyBillSubmissionEvaluationModifiers, billActivityEventLabel, billAgingBucket, billAgingDays, billLifecycleDisplayLabel, billLifecycleStage, billSubmissionsRibbonDeliveryLabel, billSubmissionsRibbonFromHistory, buildBillReviewSaveInput, buildBillStatusAgingCsv, buildBillStatusAgingMatrix, buildBillingReportCsv, buildBillingReportRows, calculateBillSubmissionAllowedAmount, chooseClaimsAdministrator, claimsAdministratorRecommendations, createBillStatusClient, createBillingOperationsClient, ensureTrailingBillSubmissionLine, ensureTrailingProcedureLine, exactClaimsAdministratorMatch, extractSendRouteEmail, formatBillSubmissionDate, formatSendRouteFax, isBillLifecycleDraft, mindBillAppearanceStyle, mindBillThemePresets, normalizeClaimsAdministratorName, organizationProfileOptions, parseBillSubmissionDate, prepareBillSubmissionDocuments, resolveMindBillAppearance, submittedClaimsAdministrator, summarizeBillingDashboard, useBillLifecycle, useBillStatus, validateBillSubmission, visibleBillLifecycleActions };
|
|
1737
|
+
export { BILL_STATUS_AGING_BUCKETS, BILL_SUBMISSION_DIAGNOSIS_QUICK_PICKS, BILL_SUBMISSION_DOCUMENT_TYPES, BILL_SUBMISSION_REPORT_TYPES, BILL_SUBMISSION_REQUIRED_FIELDS, type BillActivityEvent, BillActivityTimeline, type BillActivityTimelineProps, type BillAgingBucket, type BillAgingBucketId, BillAgingSummary, type BillAgingSummaryProps, BillCourtesyCopyForm, type BillCourtesyCopyFormProps, BillExplanationOfReview, type BillExplanationOfReviewProps, BillHistoryTable, type BillHistoryTableProps, BillLifecycleActions, type BillLifecycleActionsProps, BillLifecycleProgress, type BillLifecycleProgressProps, type BillLifecycleProgressStepsOptions, type BillLifecycleStage, BillList, type BillListProps, BillPayerContactCard, type BillPayerContactCardProps, BillPaymentLedger, type BillPaymentLedgerProps, BillReadOnlyForm, type BillReadOnlyFormProps, type BillRegistryAge, type BillRegistryItem, type BillRegistryQuery, type BillRegistryResult, type BillRegistrySort, type BillRegistryStatus, BillRejectionNotice, type BillRejectionNoticeProps, 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 BillStatusAgingCell, BillStatusAgingMatrix, type BillStatusAgingMatrixData, type BillStatusAgingMatrixProps, type BillStatusAgingRow, type BillStatusClient, type BillStatusClientOptions, type BillStatusData, type BillStatusSession, type BillStatusSessionProvider, type BillStatusSessionRequest, BillStatusSummary, type BillStatusSummaryProps, BillSubmissionActions, type BillSubmissionAddress, type BillSubmissionAttachmentReportTypeMode, BillSubmissionAttachmentsSection, BillSubmissionClaimSection, type BillSubmissionDiagnosisOption, type BillSubmissionDocumentType, type BillSubmissionEvaluationType, BillSubmissionForm, type BillSubmissionFormProps, type BillSubmissionFormValue, BillSubmissionHeader, type BillSubmissionInput$1 as BillSubmissionInput, type BillSubmissionModifierOption, BillSubmissionPatientSection, type BillSubmissionPostalPlace, type BillSubmissionProcedureOption, type BillSubmissionProfileOption, type BillSubmissionProfileOptions, BillSubmissionProvidersSection, type BillSubmissionReportTypeOption, type BillSubmissionRibbonItem, type BillSubmissionRoute, type BillSubmissionSectionId, BillSubmissionServiceLinesSection, type BillSubmissionSourceAttachment, type BillSubmissionTaxonomyOption, type BillSubmissionUpload, type BillSubmissionValidation, BillSubmissionsRibbon, type BillSubmissionsRibbonProps, BillTasksDashboard, type BillTasksDashboardCell, type BillTasksDashboardProps, type BillTasksResult, type BillingComponentProps, BillingDashboard, type BillingDashboardBill, type BillingDashboardProps, type BillingDashboardSummary, type BillingOperationsClient, type BillingOperationsClientOptions, BillingReport, type BillingReportDimension, type BillingReportProps, type BillingReportRow, BillingSettings, ClaimsAdministratorDirectoryDialog, type ClaimsAdministratorDirectoryDialogProps, ConnectedBillLifecycle, type ConnectedBillLifecycleProps, ConnectedBillSearch, type ConnectedBillSearchProps, ConnectedBillStatus, type ConnectedBillStatusProps, ConnectedBillTasksDashboard, type ConnectedBillTasksDashboardProps, ConnectedBillingWorkspace, type ConnectedBillingWorkspaceProps, ConnectedNotificationRecipientsSettings, ConnectedNotificationSettings, ConnectedPaymentReview, type ConnectedPaymentReviewProps, ConnectedProductivityReport, ConnectedServiceLineItemsReport, type CourtesyCopyRecipientOption, DEFAULT_BILL_SUBMISSION_MODIFIERS, DEFAULT_BILL_SUBMISSION_PROCEDURES, DEFAULT_BILL_SUBMISSION_TAXONOMIES, type DentalDraftContentInput, DentalDraftEditor, type DentalDraftEditorProps, type DentalDraftLineInput, MED_LEGAL_REPORT_TYPE_CODE, MindBillBillReview, MindBillBillTimeline, type MindBillReactAppearance, type MindBillThemePreset, type MindBillWidgetProps, type NotificationAgingDay, type NotificationPreferences, type NotificationRecipient, type NotificationRecipientInvitation, type NotificationRecipientsAdapter, NotificationRecipientsSettings, type NotificationRecipientsSettingsProps, type NotificationRecipientsSnapshot, type NotificationReportDigest, NotificationSettings, type NotificationSettingsAdapter, type NotificationSettingsProps, type NotificationSettingsSnapshot, type NotificationSettingsUpdate, OrganizationOnboarding, type OrganizationOnboardingProps, PSYCH_QME_DEFAULT_DIAGNOSIS, type PaymentReviewItem, type PaymentReviewQuery, type PaymentReviewRange, type PaymentReviewResult, type ProductivityReport, ReportBillStatusDialog, type ReportBillStatusDialogProps, RfaDraftForm, type RfaDraftFormProps, type RfaDraftInput, type RfaDraftItemInput, SECOND_REVIEW_REASON_TEMPLATES, SecondReviewForm, type SecondReviewFormProps, SendRouteDialog, type SendRouteDialogProps, type SendRouteSubmission, type ServiceLineItemsReport, type UseBillLifecycleOptions, type UseBillLifecycleResult, type UseBillStatusOptions, type UseBillStatusResult, type W9ExtractionStatus, W9Upload, type W9UploadProps, applyBillSubmissionEvaluationDiagnoses, applyBillSubmissionEvaluationModifiers, billActivityEventLabel, billAgingBucket, billAgingDays, billLifecycleDisplayLabel, billLifecycleStage, billSubmissionsRibbonDeliveryLabel, billSubmissionsRibbonFromHistory, buildBillReviewSaveInput, buildBillStatusAgingCsv, buildBillStatusAgingMatrix, buildBillingReportCsv, buildBillingReportRows, calculateBillSubmissionAllowedAmount, chooseClaimsAdministrator, claimsAdministratorRecommendations, createBillStatusClient, createBillingOperationsClient, dentalDraftChargeSummary, ensureTrailingBillSubmissionLine, ensureTrailingProcedureLine, exactClaimsAdministratorMatch, extractSendRouteEmail, formatBillSubmissionDate, formatSendRouteFax, isBillLifecycleDraft, mindBillAppearanceStyle, mindBillThemePresets, normalizeClaimsAdministratorName, normalizeRfaDraft, organizationProfileOptions, parseBillSubmissionDate, parseDentalCharge, prepareBillSubmissionDocuments, resolveMindBillAppearance, submittedClaimsAdministrator, summarizeBillingDashboard, useBillLifecycle, useBillStatus, validateBillSubmission, validateDentalDraftContent, validateRfaDraft, visibleBillLifecycleActions };
|
package/dist/index.js
CHANGED
|
@@ -348,7 +348,7 @@ function organizationProfileOptions(profile) {
|
|
|
348
348
|
|
|
349
349
|
// src/index.tsx
|
|
350
350
|
import "@mindbill/embed";
|
|
351
|
-
import { createElement, useEffect as useEffect16, useRef as
|
|
351
|
+
import { createElement, useEffect as useEffect16, useRef as useRef12 } from "react";
|
|
352
352
|
|
|
353
353
|
// src/native-bill-review.tsx
|
|
354
354
|
import { sanitizeBillReviewSaveInput } from "@mindbill/browser";
|
|
@@ -8401,9 +8401,408 @@ function ConnectedBillingWorkspace({ appearance, className, style, initialView =
|
|
|
8401
8401
|
] });
|
|
8402
8402
|
}
|
|
8403
8403
|
|
|
8404
|
+
// src/dental-draft-editor.tsx
|
|
8405
|
+
import { useState as useState20 } from "react";
|
|
8406
|
+
|
|
8407
|
+
// src/treatment-draft-shared.tsx
|
|
8408
|
+
import { useRef as useRef11, useState as useState19 } from "react";
|
|
8409
|
+
import { jsx as jsx23, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
8410
|
+
function useDraftSave(onSave) {
|
|
8411
|
+
const pending = useRef11(false);
|
|
8412
|
+
const [busy, setBusy] = useState19(false);
|
|
8413
|
+
const [message, setMessage] = useState19("");
|
|
8414
|
+
const [error, setError] = useState19(null);
|
|
8415
|
+
const clear = () => {
|
|
8416
|
+
setMessage("");
|
|
8417
|
+
setError(null);
|
|
8418
|
+
};
|
|
8419
|
+
const save = async (value, failure) => {
|
|
8420
|
+
if (pending.current) return;
|
|
8421
|
+
clear();
|
|
8422
|
+
if (failure) {
|
|
8423
|
+
setError(failure);
|
|
8424
|
+
return;
|
|
8425
|
+
}
|
|
8426
|
+
pending.current = true;
|
|
8427
|
+
setBusy(true);
|
|
8428
|
+
try {
|
|
8429
|
+
await onSave(value);
|
|
8430
|
+
setMessage("Draft saved.");
|
|
8431
|
+
} catch {
|
|
8432
|
+
setError("The draft could not be saved. Review its status before retrying.");
|
|
8433
|
+
} finally {
|
|
8434
|
+
pending.current = false;
|
|
8435
|
+
setBusy(false);
|
|
8436
|
+
}
|
|
8437
|
+
};
|
|
8438
|
+
return { busy, message, error, clear, save };
|
|
8439
|
+
}
|
|
8440
|
+
function TreatmentDraftShell({ title, description, children, appearance, className = "", style }) {
|
|
8441
|
+
return /* @__PURE__ */ jsxs23("section", { className: `mbtd ${className}`.trim(), "aria-label": title, style: { ...mindBillAppearanceStyle(appearance), ...style }, children: [
|
|
8442
|
+
/* @__PURE__ */ jsx23("style", { children: `.mbtd{font:14px/1.5 ui-sans-serif,system-ui,sans-serif;color:var(--mb-text);min-width:0}.mbtd *{box-sizing:border-box}.mbtd h2{font-size:22px;margin:0 0 6px}.mbtd p{margin:6px 0 16px;color:var(--mb-muted)}.mbtd form{display:grid;gap:16px}.mbtd fieldset{margin:0;padding:16px;border:1px solid var(--mb-border);border-radius:var(--mb-radius);background:var(--mb-surface);min-width:0}.mbtd legend{font-weight:600;padding:0 6px}.mbtd .mbtd-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}.mbtd label{display:grid;gap:5px;font-size:13px;min-width:0}.mbtd input,.mbtd select,.mbtd textarea,.mbtd button{font:inherit;max-width:100%;min-width:0;border:1px solid var(--mb-border);border-radius:var(--mb-control-radius);padding:9px 11px;color:var(--mb-text);background:var(--mb-input);min-height:40px}.mbtd textarea{resize:vertical;min-height:76px}.mbtd button{cursor:pointer;background:var(--mb-surface)}.mbtd button:disabled{opacity:.55;cursor:default}.mbtd .mbtd-primary{background:var(--mb-accent);color:var(--mb-accent-contrast);border-color:var(--mb-accent)}.mbtd .mbtd-actions{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-top:12px}.mbtd .mbtd-wide{grid-column:1/-1}.mbtd [role=alert]{color:var(--mb-danger)}.mbtd [role=status]{margin:0}.mbtd :is(input,select,textarea,button):focus-visible{outline:2px solid var(--mb-accent);outline-offset:2px}.mbtd .mbtd-note{padding:12px;background:var(--mb-soft);border-radius:var(--mb-control-radius)}.mbtd .mbtd-attribution{font-size:12px;text-align:right}.mbtd a{color:var(--mb-accent)}@media(max-width:520px){.mbtd .mbtd-grid{grid-template-columns:minmax(0,1fr)}.mbtd fieldset{padding:12px}}` }),
|
|
8443
|
+
/* @__PURE__ */ jsx23("h2", { children: title }),
|
|
8444
|
+
/* @__PURE__ */ jsx23("p", { children: description }),
|
|
8445
|
+
children,
|
|
8446
|
+
/* @__PURE__ */ jsx23("p", { className: "mbtd-attribution", children: /* @__PURE__ */ jsx23("a", { href: "https://mindbill.org", target: "_blank", rel: "noopener noreferrer", children: "Powered by MindBill" }) })
|
|
8447
|
+
] });
|
|
8448
|
+
}
|
|
8449
|
+
function isDraftDate(value) {
|
|
8450
|
+
return /^\d{4}-\d{2}-\d{2}$/.test(value) && Number.isFinite(Date.parse(value)) && new Date(value).toISOString().slice(0, 10) === value;
|
|
8451
|
+
}
|
|
8452
|
+
|
|
8453
|
+
// src/dental-draft-editor.tsx
|
|
8454
|
+
import { jsx as jsx24, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
8455
|
+
function dentalDraftChargeSummary(lines) {
|
|
8456
|
+
const knownChargeCents = lines.reduce((sum, line) => sum + (line.chargeCents ?? 0), 0);
|
|
8457
|
+
return { knownChargeCents, totalChargeCents: lines.some((line) => line.chargeCents === null) ? null : knownChargeCents };
|
|
8458
|
+
}
|
|
8459
|
+
function parseDentalCharge(value) {
|
|
8460
|
+
if (!value.trim()) return null;
|
|
8461
|
+
if (!/^\d+(?:\.\d{1,2})?$/.test(value)) throw new Error("Enter a positive charge with at most two decimal places, or leave it blank.");
|
|
8462
|
+
const [whole = "0", fraction = ""] = value.split(".");
|
|
8463
|
+
const cents = Number(whole) * 100 + Number(fraction.padEnd(2, "0"));
|
|
8464
|
+
if (!Number.isSafeInteger(cents) || cents < 1 || cents > 1e9) throw new Error("Enter a charge between $0.01 and $10,000,000, or leave it blank.");
|
|
8465
|
+
return cents;
|
|
8466
|
+
}
|
|
8467
|
+
function validateDentalDraftContent(content) {
|
|
8468
|
+
if (!content.renderingProviderId.trim() || content.renderingProviderId.length > 255) return "Choose a rendering provider in the host application.";
|
|
8469
|
+
if (content.billingProviderId != null && (!content.billingProviderId.trim() || content.billingProviderId.length > 255)) return "Choose a valid billing provider in the host application.";
|
|
8470
|
+
if (content.lines.length > 100) return "A draft can contain up to 100 service lines.";
|
|
8471
|
+
if (content.diagnosisCodes.length > 12 || content.diagnosisCodes.some((code) => !code.trim() || code.length > 20)) return "Enter up to 12 diagnosis codes of at most 20 characters each.";
|
|
8472
|
+
if (content.notes !== null && (!content.notes.trim() || content.notes.length > 5e3)) return "Notes must contain between 1 and 5,000 characters.";
|
|
8473
|
+
for (const [index, line] of content.lines.entries()) {
|
|
8474
|
+
const prefix = `Line ${index + 1}: `;
|
|
8475
|
+
if (line.code !== null && !/^D[0-9]{4}$/.test(line.code)) return prefix + "enter D followed by four digits, or leave the code blank.";
|
|
8476
|
+
if (!Number.isInteger(line.quantity) || line.quantity < 1 || line.quantity > 1e5) return prefix + "enter a whole quantity between 1 and 100,000.";
|
|
8477
|
+
if (line.chargeCents !== null && (!Number.isSafeInteger(line.chargeCents) || line.chargeCents < 1 || line.chargeCents > 1e9)) return prefix + "enter a positive extended charge, or leave it unknown.";
|
|
8478
|
+
if (line.serviceDate !== null && !isDraftDate(line.serviceDate)) return prefix + "enter a valid service date.";
|
|
8479
|
+
if (line.editionYear !== null && (!Number.isInteger(line.editionYear) || line.editionYear < 1900 || line.editionYear > 2200)) return prefix + "enter an edition year between 1900 and 2200.";
|
|
8480
|
+
for (const [value, limit] of [[line.description, 1e3], [line.chargeReference, 1e3], [line.oralCavity, 100], [line.prosthesisNotes, 2e3]]) {
|
|
8481
|
+
if (value !== null && (!value.trim() || value.length > limit)) return prefix + `text must contain between 1 and ${limit} characters.`;
|
|
8482
|
+
}
|
|
8483
|
+
if (line.teeth.length > 32 || line.surfaces.length > 10 || [...line.teeth, ...line.surfaces].some((value) => !value.trim() || value.length > 20)) return prefix + "check tooth and surface entries (up to 32 teeth and 10 surfaces, 20 characters each).";
|
|
8484
|
+
}
|
|
8485
|
+
return null;
|
|
8486
|
+
}
|
|
8487
|
+
var blankLine2 = () => ({ code: null, description: null, editionYear: null, serviceDate: null, quantity: 1, chargeCents: null, chargeReference: null, teeth: [], surfaces: [], oralCavity: null, prosthesisNotes: null });
|
|
8488
|
+
var nullable = (value) => value === "" ? null : value;
|
|
8489
|
+
var split = (value) => value.split(",").map((part) => part.trim()).filter(Boolean);
|
|
8490
|
+
var currency2 = (cents) => new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(cents / 100);
|
|
8491
|
+
function DentalDraftEditor({ initialContent, onSave, disabled = false, ...appearance }) {
|
|
8492
|
+
const [content, setContent] = useState20(() => structuredClone(initialContent));
|
|
8493
|
+
const [charges, setCharges] = useState20(() => initialContent.lines.map((line) => line.chargeCents === null ? "" : (line.chargeCents / 100).toFixed(2)));
|
|
8494
|
+
const [diagnoses, setDiagnoses] = useState20(() => initialContent.diagnosisCodes.join(", "));
|
|
8495
|
+
const [teeth, setTeeth] = useState20(() => initialContent.lines.map((line) => line.teeth.join(", ")));
|
|
8496
|
+
const [surfaces, setSurfaces] = useState20(() => initialContent.lines.map((line) => line.surfaces.join(", ")));
|
|
8497
|
+
const action = useDraftSave(onSave);
|
|
8498
|
+
const locked = disabled || action.busy;
|
|
8499
|
+
const updateLine = (index, patch) => {
|
|
8500
|
+
action.clear();
|
|
8501
|
+
setContent((current) => ({ ...current, lines: current.lines.map((line, position) => position === index ? { ...line, ...patch } : line) }));
|
|
8502
|
+
};
|
|
8503
|
+
const parsed = () => ({ ...content, diagnosisCodes: split(diagnoses), lines: content.lines.map((line, index) => ({ ...line, teeth: split(teeth[index] ?? ""), surfaces: split(surfaces[index] ?? ""), chargeCents: parseDentalCharge(charges[index] ?? "") })) });
|
|
8504
|
+
let summary = null;
|
|
8505
|
+
try {
|
|
8506
|
+
summary = dentalDraftChargeSummary(parsed().lines);
|
|
8507
|
+
} catch {
|
|
8508
|
+
}
|
|
8509
|
+
return /* @__PURE__ */ jsxs24(TreatmentDraftShell, { ...appearance, title: "Dental draft", description: "Record dental services, tooth details, and practice charges for review.", children: [
|
|
8510
|
+
/* @__PURE__ */ jsx24("p", { className: "mbtd-note", children: "Codes and prices need review. Saving does not send a dental claim. A blank charge remains unknown." }),
|
|
8511
|
+
/* @__PURE__ */ jsxs24("form", { onSubmit: (event) => {
|
|
8512
|
+
event.preventDefault();
|
|
8513
|
+
if (locked) return;
|
|
8514
|
+
let next;
|
|
8515
|
+
try {
|
|
8516
|
+
next = parsed();
|
|
8517
|
+
} catch {
|
|
8518
|
+
void action.save(content, "Enter positive charges with at most two decimal places, or leave them blank.");
|
|
8519
|
+
return;
|
|
8520
|
+
}
|
|
8521
|
+
void action.save(next, validateDentalDraftContent(next));
|
|
8522
|
+
}, children: [
|
|
8523
|
+
/* @__PURE__ */ jsxs24("fieldset", { disabled: locked, children: [
|
|
8524
|
+
/* @__PURE__ */ jsx24("legend", { children: "Claim details" }),
|
|
8525
|
+
/* @__PURE__ */ jsxs24("div", { className: "mbtd-grid", children: [
|
|
8526
|
+
/* @__PURE__ */ jsxs24("label", { children: [
|
|
8527
|
+
"Diagnosis codes, comma separated",
|
|
8528
|
+
/* @__PURE__ */ jsx24("input", { value: diagnoses, onChange: (event) => {
|
|
8529
|
+
action.clear();
|
|
8530
|
+
setDiagnoses(event.target.value);
|
|
8531
|
+
} })
|
|
8532
|
+
] }),
|
|
8533
|
+
/* @__PURE__ */ jsxs24("label", { children: [
|
|
8534
|
+
"Notes",
|
|
8535
|
+
/* @__PURE__ */ jsx24("textarea", { maxLength: 5e3, value: content.notes ?? "", onChange: (event) => {
|
|
8536
|
+
action.clear();
|
|
8537
|
+
setContent({ ...content, notes: nullable(event.target.value) });
|
|
8538
|
+
} })
|
|
8539
|
+
] })
|
|
8540
|
+
] })
|
|
8541
|
+
] }),
|
|
8542
|
+
content.lines.map((line, index) => /* @__PURE__ */ jsxs24("fieldset", { disabled: locked, children: [
|
|
8543
|
+
/* @__PURE__ */ jsxs24("legend", { children: [
|
|
8544
|
+
"Service ",
|
|
8545
|
+
index + 1
|
|
8546
|
+
] }),
|
|
8547
|
+
/* @__PURE__ */ jsxs24("div", { className: "mbtd-grid", children: [
|
|
8548
|
+
/* @__PURE__ */ jsxs24("label", { children: [
|
|
8549
|
+
"Dental code",
|
|
8550
|
+
/* @__PURE__ */ jsx24("input", { placeholder: "D0120", maxLength: 5, value: line.code ?? "", onChange: (event) => updateLine(index, { code: nullable(event.target.value.toUpperCase()) }) })
|
|
8551
|
+
] }),
|
|
8552
|
+
/* @__PURE__ */ jsxs24("label", { children: [
|
|
8553
|
+
"Code edition year",
|
|
8554
|
+
/* @__PURE__ */ jsx24("input", { type: "number", min: 1900, max: 2200, value: line.editionYear ?? "", onChange: (event) => updateLine(index, { editionYear: event.target.value === "" ? null : Number(event.target.value) }) })
|
|
8555
|
+
] }),
|
|
8556
|
+
/* @__PURE__ */ jsxs24("label", { children: [
|
|
8557
|
+
"Date of service",
|
|
8558
|
+
/* @__PURE__ */ jsx24("input", { type: "date", value: line.serviceDate ?? "", onChange: (event) => updateLine(index, { serviceDate: nullable(event.target.value) }) })
|
|
8559
|
+
] }),
|
|
8560
|
+
/* @__PURE__ */ jsxs24("label", { children: [
|
|
8561
|
+
"Quantity",
|
|
8562
|
+
/* @__PURE__ */ jsx24("input", { type: "number", min: 1, max: 1e5, step: 1, value: line.quantity, onChange: (event) => updateLine(index, { quantity: Number(event.target.value) }) })
|
|
8563
|
+
] }),
|
|
8564
|
+
/* @__PURE__ */ jsxs24("label", { children: [
|
|
8565
|
+
"Extended line charge ($)",
|
|
8566
|
+
/* @__PURE__ */ jsx24("input", { inputMode: "decimal", placeholder: "Unknown", value: charges[index] ?? "", onChange: (event) => {
|
|
8567
|
+
action.clear();
|
|
8568
|
+
setCharges(charges.map((value, position) => position === index ? event.target.value : value));
|
|
8569
|
+
} }),
|
|
8570
|
+
/* @__PURE__ */ jsx24("small", { children: "Includes all units. This is the practice charge, not a fee allowance." })
|
|
8571
|
+
] }),
|
|
8572
|
+
/* @__PURE__ */ jsxs24("label", { children: [
|
|
8573
|
+
"Charge source / reference",
|
|
8574
|
+
/* @__PURE__ */ jsx24("input", { maxLength: 1e3, value: line.chargeReference ?? "", onChange: (event) => updateLine(index, { chargeReference: nullable(event.target.value) }) })
|
|
8575
|
+
] }),
|
|
8576
|
+
/* @__PURE__ */ jsxs24("label", { children: [
|
|
8577
|
+
"Teeth, comma separated",
|
|
8578
|
+
/* @__PURE__ */ jsx24("input", { value: teeth[index] ?? "", onChange: (event) => {
|
|
8579
|
+
action.clear();
|
|
8580
|
+
setTeeth(teeth.map((value, position) => position === index ? event.target.value : value));
|
|
8581
|
+
} })
|
|
8582
|
+
] }),
|
|
8583
|
+
/* @__PURE__ */ jsxs24("label", { children: [
|
|
8584
|
+
"Surfaces, comma separated",
|
|
8585
|
+
/* @__PURE__ */ jsx24("input", { value: surfaces[index] ?? "", onChange: (event) => {
|
|
8586
|
+
action.clear();
|
|
8587
|
+
setSurfaces(surfaces.map((value, position) => position === index ? event.target.value.toUpperCase() : value));
|
|
8588
|
+
} })
|
|
8589
|
+
] }),
|
|
8590
|
+
/* @__PURE__ */ jsxs24("label", { children: [
|
|
8591
|
+
"Oral cavity",
|
|
8592
|
+
/* @__PURE__ */ jsx24("input", { maxLength: 100, value: line.oralCavity ?? "", onChange: (event) => updateLine(index, { oralCavity: nullable(event.target.value) }) })
|
|
8593
|
+
] }),
|
|
8594
|
+
/* @__PURE__ */ jsxs24("label", { children: [
|
|
8595
|
+
"Service description",
|
|
8596
|
+
/* @__PURE__ */ jsx24("input", { maxLength: 1e3, value: line.description ?? "", onChange: (event) => updateLine(index, { description: nullable(event.target.value) }) })
|
|
8597
|
+
] }),
|
|
8598
|
+
/* @__PURE__ */ jsxs24("label", { className: "mbtd-wide", children: [
|
|
8599
|
+
"Prosthesis notes",
|
|
8600
|
+
/* @__PURE__ */ jsx24("textarea", { maxLength: 2e3, value: line.prosthesisNotes ?? "", onChange: (event) => updateLine(index, { prosthesisNotes: nullable(event.target.value) }) })
|
|
8601
|
+
] })
|
|
8602
|
+
] }),
|
|
8603
|
+
/* @__PURE__ */ jsx24("div", { className: "mbtd-actions", children: /* @__PURE__ */ jsxs24("button", { type: "button", onClick: () => {
|
|
8604
|
+
action.clear();
|
|
8605
|
+
setContent({ ...content, lines: content.lines.filter((_, position) => position !== index) });
|
|
8606
|
+
setCharges(charges.filter((_, position) => position !== index));
|
|
8607
|
+
setTeeth(teeth.filter((_, position) => position !== index));
|
|
8608
|
+
setSurfaces(surfaces.filter((_, position) => position !== index));
|
|
8609
|
+
}, children: [
|
|
8610
|
+
"Remove service ",
|
|
8611
|
+
index + 1
|
|
8612
|
+
] }) })
|
|
8613
|
+
] }, index)),
|
|
8614
|
+
/* @__PURE__ */ jsx24("div", { children: /* @__PURE__ */ jsx24("button", { type: "button", disabled: locked || content.lines.length >= 100, onClick: () => {
|
|
8615
|
+
action.clear();
|
|
8616
|
+
setContent({ ...content, lines: [...content.lines, blankLine2()] });
|
|
8617
|
+
setCharges([...charges, ""]);
|
|
8618
|
+
setTeeth([...teeth, ""]);
|
|
8619
|
+
setSurfaces([...surfaces, ""]);
|
|
8620
|
+
}, children: "Add dental service" }) }),
|
|
8621
|
+
/* @__PURE__ */ jsx24("p", { role: "status", children: summary ? summary.totalChargeCents === null ? `Known charges: ${currency2(summary.knownChargeCents)} \xB7 Total incomplete` : `Total practice charge: ${currency2(summary.totalChargeCents)}` : "Review charge amounts before saving." }),
|
|
8622
|
+
action.error ? /* @__PURE__ */ jsx24("p", { role: "alert", children: action.error }) : null,
|
|
8623
|
+
/* @__PURE__ */ jsxs24("div", { className: "mbtd-actions", children: [
|
|
8624
|
+
/* @__PURE__ */ jsx24("button", { type: "submit", className: "mbtd-primary", disabled: locked, children: action.busy ? "Saving\u2026" : "Save dental draft" }),
|
|
8625
|
+
/* @__PURE__ */ jsx24("p", { role: "status", children: action.message })
|
|
8626
|
+
] })
|
|
8627
|
+
] })
|
|
8628
|
+
] });
|
|
8629
|
+
}
|
|
8630
|
+
|
|
8631
|
+
// src/rfa-draft-form.tsx
|
|
8632
|
+
import { useState as useState21 } from "react";
|
|
8633
|
+
import { jsx as jsx25, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
8634
|
+
function normalizeRfaDraft(draft) {
|
|
8635
|
+
const copy = structuredClone(draft);
|
|
8636
|
+
for (const item of copy.items) {
|
|
8637
|
+
for (const key of ["externalId", "procedureCode", "frequency", "duration", "requestedFrom", "requestedTo"]) {
|
|
8638
|
+
if (item[key] !== void 0 && !item[key]?.trim()) delete item[key];
|
|
8639
|
+
}
|
|
8640
|
+
}
|
|
8641
|
+
delete copy.signedAt;
|
|
8642
|
+
return copy;
|
|
8643
|
+
}
|
|
8644
|
+
function validateRfaDraft(draft) {
|
|
8645
|
+
for (const value of [draft.claimId, draft.patientId, draft.renderingProviderId]) if (!value.trim() || value.length > 255) return "Select a claim, patient, and rendering provider in the host application.";
|
|
8646
|
+
for (const value of [draft.externalId, draft.claimsAdminId]) if (value !== void 0 && (!value.trim() || value.length > 255)) return "Check the request and claims administrator identifiers.";
|
|
8647
|
+
for (const value of [draft.employeeName, draft.providerName]) if (!value.trim() || value.length > 200) return "Employee and provider names are required (up to 200 characters).";
|
|
8648
|
+
if (draft.requestType !== void 0 && !["new", "resubmission_material_change", "oral_authorization_confirmation"].includes(draft.requestType)) return "Choose a valid request type.";
|
|
8649
|
+
if (draft.reviewType !== void 0 && !["prospective", "concurrent", "retrospective"].includes(draft.reviewType)) return "Choose a valid review type.";
|
|
8650
|
+
if (draft.requestType === "resubmission_material_change" && !draft.materialChange?.trim()) return "Describe the material change for this resubmission.";
|
|
8651
|
+
if ((draft.rationale?.length ?? 0) > 2e4 || (draft.materialChange?.length ?? 0) > 2e4) return "Rationale and material change must each be at most 20,000 characters.";
|
|
8652
|
+
if (draft.placeOfServiceCode !== void 0 && !/^\d{2}$/.test(draft.placeOfServiceCode)) return "Place of service must contain two digits.";
|
|
8653
|
+
if ((draft.providerNpi?.length ?? 0) > 20 || (draft.providerPhone?.length ?? 0) > 30 || (draft.claimNumber?.length ?? 0) > 100) return "Check provider contact and claim number lengths.";
|
|
8654
|
+
if (draft.providerFax && (draft.providerFax.length > 30 || !/^[+\d().\s-]+$/.test(draft.providerFax) || !/^\d{10,15}$/.test(draft.providerFax.replace(/\D/g, "")))) return "Enter a return fax with 10 to 15 digits, or leave it blank.";
|
|
8655
|
+
if (draft.dateOfInjury !== void 0 && !isDraftDate(draft.dateOfInjury)) return "Check the injury date in the host application.";
|
|
8656
|
+
if (draft.items.length < 1 || draft.items.length > 100) return "Include between 1 and 100 requested services.";
|
|
8657
|
+
for (const [index, item] of draft.items.entries()) {
|
|
8658
|
+
const prefix = `Service ${index + 1}: `;
|
|
8659
|
+
if (!item.diagnosisCode.trim() || item.diagnosisCode.length > 16 || !item.serviceDescription.trim() || item.serviceDescription.length > 1e3) return prefix + "a diagnosis code and service description are required.";
|
|
8660
|
+
for (const [value, max] of [[item.externalId, 255], [item.procedureCode, 16], [item.frequency, 200], [item.duration, 200]]) if (value !== void 0 && (!value.trim() || value.length > max)) return prefix + `optional text must contain 1 to ${max} characters.`;
|
|
8661
|
+
if (item.units !== void 0 && (!Number.isInteger(item.units) || item.units < 1 || item.units > 1e5)) return prefix + "units must be a whole number between 1 and 100,000.";
|
|
8662
|
+
if (item.quantity !== void 0 && (!Number.isFinite(item.quantity) || item.quantity <= 0 || item.quantity > 1e5)) return prefix + "quantity must be positive and at most 100,000.";
|
|
8663
|
+
if (item.requestedFrom !== void 0 && !isDraftDate(item.requestedFrom) || item.requestedTo !== void 0 && (!isDraftDate(item.requestedTo) || !item.requestedFrom || item.requestedTo < item.requestedFrom)) return prefix + "enter valid dates with the end on or after the start.";
|
|
8664
|
+
}
|
|
8665
|
+
return null;
|
|
8666
|
+
}
|
|
8667
|
+
function RfaDraftForm({ initialDraft, onSave, disabled = false, ...appearance }) {
|
|
8668
|
+
const [draft, setDraft] = useState21(() => normalizeRfaDraft(initialDraft));
|
|
8669
|
+
const action = useDraftSave(onSave), locked = disabled || action.busy;
|
|
8670
|
+
const update = (patch) => {
|
|
8671
|
+
action.clear();
|
|
8672
|
+
setDraft((current) => ({ ...current, ...patch }));
|
|
8673
|
+
};
|
|
8674
|
+
const updateItem = (index, patch) => {
|
|
8675
|
+
action.clear();
|
|
8676
|
+
setDraft((current) => ({ ...current, items: current.items.map((item, position) => position === index ? { ...item, ...patch } : item) }));
|
|
8677
|
+
};
|
|
8678
|
+
const updateNumber = (index, key, value) => {
|
|
8679
|
+
action.clear();
|
|
8680
|
+
setDraft((current) => ({ ...current, items: current.items.map((item, position) => {
|
|
8681
|
+
if (position !== index) return item;
|
|
8682
|
+
const next = { ...item };
|
|
8683
|
+
if (value === "") delete next[key];
|
|
8684
|
+
else next[key] = Number(value);
|
|
8685
|
+
return next;
|
|
8686
|
+
}) }));
|
|
8687
|
+
};
|
|
8688
|
+
return /* @__PURE__ */ jsxs25(TreatmentDraftShell, { ...appearance, title: "Request for authorization", description: "Prepare services and supporting rationale for utilization review.", children: [
|
|
8689
|
+
/* @__PURE__ */ jsx25("p", { className: "mbtd-note", children: "Saving creates an unsigned draft. It does not send the request or authorize treatment." }),
|
|
8690
|
+
/* @__PURE__ */ jsxs25("form", { onSubmit: (event) => {
|
|
8691
|
+
event.preventDefault();
|
|
8692
|
+
if (locked) return;
|
|
8693
|
+
const next = normalizeRfaDraft(draft);
|
|
8694
|
+
void action.save(next, validateRfaDraft(next));
|
|
8695
|
+
}, children: [
|
|
8696
|
+
/* @__PURE__ */ jsxs25("fieldset", { disabled: locked, children: [
|
|
8697
|
+
/* @__PURE__ */ jsx25("legend", { children: "Request details" }),
|
|
8698
|
+
/* @__PURE__ */ jsxs25("div", { className: "mbtd-grid", children: [
|
|
8699
|
+
/* @__PURE__ */ jsxs25("label", { children: [
|
|
8700
|
+
"Employee",
|
|
8701
|
+
/* @__PURE__ */ jsx25("input", { readOnly: true, value: draft.employeeName })
|
|
8702
|
+
] }),
|
|
8703
|
+
/* @__PURE__ */ jsxs25("label", { children: [
|
|
8704
|
+
"Requesting provider",
|
|
8705
|
+
/* @__PURE__ */ jsx25("input", { readOnly: true, value: draft.providerName })
|
|
8706
|
+
] }),
|
|
8707
|
+
/* @__PURE__ */ jsxs25("label", { children: [
|
|
8708
|
+
"Request type",
|
|
8709
|
+
/* @__PURE__ */ jsxs25("select", { value: draft.requestType ?? "new", onChange: (event) => update({ requestType: event.target.value }), children: [
|
|
8710
|
+
/* @__PURE__ */ jsx25("option", { value: "new", children: "New request" }),
|
|
8711
|
+
/* @__PURE__ */ jsx25("option", { value: "resubmission_material_change", children: "Resubmission with material change" }),
|
|
8712
|
+
/* @__PURE__ */ jsx25("option", { value: "oral_authorization_confirmation", children: "Confirm oral authorization" })
|
|
8713
|
+
] })
|
|
8714
|
+
] }),
|
|
8715
|
+
/* @__PURE__ */ jsxs25("label", { children: [
|
|
8716
|
+
"Review type",
|
|
8717
|
+
/* @__PURE__ */ jsxs25("select", { value: draft.reviewType ?? "prospective", onChange: (event) => update({ reviewType: event.target.value }), children: [
|
|
8718
|
+
/* @__PURE__ */ jsx25("option", { value: "prospective", children: "Prospective \u2014 before treatment" }),
|
|
8719
|
+
/* @__PURE__ */ jsx25("option", { value: "concurrent", children: "Concurrent \u2014 during treatment" }),
|
|
8720
|
+
/* @__PURE__ */ jsx25("option", { value: "retrospective", children: "Retrospective \u2014 after treatment" })
|
|
8721
|
+
] })
|
|
8722
|
+
] }),
|
|
8723
|
+
/* @__PURE__ */ jsxs25("label", { children: [
|
|
8724
|
+
"Review priority",
|
|
8725
|
+
/* @__PURE__ */ jsxs25("select", { value: draft.expedited ? "expedited" : "standard", onChange: (event) => update({ expedited: event.target.value === "expedited" }), children: [
|
|
8726
|
+
/* @__PURE__ */ jsx25("option", { value: "standard", children: "Standard" }),
|
|
8727
|
+
/* @__PURE__ */ jsx25("option", { value: "expedited", children: "Expedited review requested" })
|
|
8728
|
+
] })
|
|
8729
|
+
] }),
|
|
8730
|
+
/* @__PURE__ */ jsxs25("label", { children: [
|
|
8731
|
+
"Return fax",
|
|
8732
|
+
/* @__PURE__ */ jsx25("input", { maxLength: 30, value: draft.providerFax ?? "", onChange: (event) => update({ providerFax: event.target.value }) }),
|
|
8733
|
+
/* @__PURE__ */ jsx25("small", { children: "Review the return contact. Organization defaults are not applied automatically." })
|
|
8734
|
+
] }),
|
|
8735
|
+
/* @__PURE__ */ jsxs25("label", { className: "mbtd-wide", children: [
|
|
8736
|
+
"Clinical rationale",
|
|
8737
|
+
/* @__PURE__ */ jsx25("textarea", { maxLength: 2e4, value: draft.rationale ?? "", onChange: (event) => update({ rationale: event.target.value }) })
|
|
8738
|
+
] }),
|
|
8739
|
+
draft.requestType === "resubmission_material_change" ? /* @__PURE__ */ jsxs25("label", { className: "mbtd-wide", children: [
|
|
8740
|
+
"Material change (required)",
|
|
8741
|
+
/* @__PURE__ */ jsx25("textarea", { required: true, maxLength: 2e4, value: draft.materialChange ?? "", onChange: (event) => update({ materialChange: event.target.value }) })
|
|
8742
|
+
] }) : null
|
|
8743
|
+
] })
|
|
8744
|
+
] }),
|
|
8745
|
+
draft.items.map((item, index) => /* @__PURE__ */ jsxs25("fieldset", { disabled: locked, children: [
|
|
8746
|
+
/* @__PURE__ */ jsxs25("legend", { children: [
|
|
8747
|
+
"Requested service ",
|
|
8748
|
+
index + 1
|
|
8749
|
+
] }),
|
|
8750
|
+
/* @__PURE__ */ jsxs25("div", { className: "mbtd-grid", children: [
|
|
8751
|
+
/* @__PURE__ */ jsxs25("label", { children: [
|
|
8752
|
+
"Diagnosis code",
|
|
8753
|
+
/* @__PURE__ */ jsx25("input", { required: true, maxLength: 16, value: item.diagnosisCode, onChange: (event) => updateItem(index, { diagnosisCode: event.target.value }) })
|
|
8754
|
+
] }),
|
|
8755
|
+
/* @__PURE__ */ jsxs25("label", { children: [
|
|
8756
|
+
"Procedure code",
|
|
8757
|
+
/* @__PURE__ */ jsx25("input", { maxLength: 16, value: item.procedureCode ?? "", onChange: (event) => updateItem(index, { procedureCode: event.target.value }) })
|
|
8758
|
+
] }),
|
|
8759
|
+
/* @__PURE__ */ jsxs25("label", { className: "mbtd-wide", children: [
|
|
8760
|
+
"Service description",
|
|
8761
|
+
/* @__PURE__ */ jsx25("textarea", { required: true, maxLength: 1e3, value: item.serviceDescription, onChange: (event) => updateItem(index, { serviceDescription: event.target.value }) })
|
|
8762
|
+
] }),
|
|
8763
|
+
/* @__PURE__ */ jsxs25("label", { children: [
|
|
8764
|
+
"Quantity",
|
|
8765
|
+
/* @__PURE__ */ jsx25("input", { type: "number", min: 1e-3, max: 1e5, step: "any", value: item.quantity ?? "", onChange: (event) => updateNumber(index, "quantity", event.target.value) })
|
|
8766
|
+
] }),
|
|
8767
|
+
/* @__PURE__ */ jsxs25("label", { children: [
|
|
8768
|
+
"Units",
|
|
8769
|
+
/* @__PURE__ */ jsx25("input", { type: "number", min: 1, max: 1e5, step: 1, value: item.units ?? "", onChange: (event) => updateNumber(index, "units", event.target.value) })
|
|
8770
|
+
] }),
|
|
8771
|
+
/* @__PURE__ */ jsxs25("label", { children: [
|
|
8772
|
+
"Frequency",
|
|
8773
|
+
/* @__PURE__ */ jsx25("input", { maxLength: 200, value: item.frequency ?? "", onChange: (event) => updateItem(index, { frequency: event.target.value }) })
|
|
8774
|
+
] }),
|
|
8775
|
+
/* @__PURE__ */ jsxs25("label", { children: [
|
|
8776
|
+
"Duration",
|
|
8777
|
+
/* @__PURE__ */ jsx25("input", { maxLength: 200, value: item.duration ?? "", onChange: (event) => updateItem(index, { duration: event.target.value }) })
|
|
8778
|
+
] }),
|
|
8779
|
+
/* @__PURE__ */ jsxs25("label", { children: [
|
|
8780
|
+
"Requested from",
|
|
8781
|
+
/* @__PURE__ */ jsx25("input", { type: "date", value: item.requestedFrom ?? "", onChange: (event) => updateItem(index, { requestedFrom: event.target.value }) })
|
|
8782
|
+
] }),
|
|
8783
|
+
/* @__PURE__ */ jsxs25("label", { children: [
|
|
8784
|
+
"Requested through",
|
|
8785
|
+
/* @__PURE__ */ jsx25("input", { type: "date", value: item.requestedTo ?? "", onChange: (event) => updateItem(index, { requestedTo: event.target.value }) })
|
|
8786
|
+
] })
|
|
8787
|
+
] }),
|
|
8788
|
+
/* @__PURE__ */ jsxs25("button", { type: "button", disabled: draft.items.length === 1, onClick: () => update({ items: draft.items.filter((_, position) => position !== index) }), children: [
|
|
8789
|
+
"Remove requested service ",
|
|
8790
|
+
index + 1
|
|
8791
|
+
] })
|
|
8792
|
+
] }, index)),
|
|
8793
|
+
/* @__PURE__ */ jsx25("div", { children: /* @__PURE__ */ jsx25("button", { type: "button", disabled: locked || draft.items.length >= 100, onClick: () => update({ items: [...draft.items, { diagnosisCode: "", serviceDescription: "" }] }), children: "Add requested service" }) }),
|
|
8794
|
+
action.error ? /* @__PURE__ */ jsx25("p", { role: "alert", children: action.error }) : null,
|
|
8795
|
+
/* @__PURE__ */ jsxs25("div", { className: "mbtd-actions", children: [
|
|
8796
|
+
/* @__PURE__ */ jsx25("button", { type: "submit", className: "mbtd-primary", disabled: locked, children: action.busy ? "Saving\u2026" : "Save RFA draft" }),
|
|
8797
|
+
/* @__PURE__ */ jsx25("p", { role: "status", children: action.message })
|
|
8798
|
+
] })
|
|
8799
|
+
] })
|
|
8800
|
+
] });
|
|
8801
|
+
}
|
|
8802
|
+
|
|
8404
8803
|
// src/index.tsx
|
|
8405
8804
|
function widget(tagName, props) {
|
|
8406
|
-
const ref =
|
|
8805
|
+
const ref = useRef12(null);
|
|
8407
8806
|
const { onMindBill, onMindBillError } = props;
|
|
8408
8807
|
useEffect16(() => {
|
|
8409
8808
|
const element = ref.current;
|
|
@@ -8492,6 +8891,7 @@ export {
|
|
|
8492
8891
|
DEFAULT_BILL_SUBMISSION_MODIFIERS,
|
|
8493
8892
|
DEFAULT_BILL_SUBMISSION_PROCEDURES,
|
|
8494
8893
|
DEFAULT_BILL_SUBMISSION_TAXONOMIES,
|
|
8894
|
+
DentalDraftEditor,
|
|
8495
8895
|
MED_LEGAL_REPORT_TYPE_CODE,
|
|
8496
8896
|
MindBillBillReview,
|
|
8497
8897
|
MindBillBillTimeline,
|
|
@@ -8501,6 +8901,7 @@ export {
|
|
|
8501
8901
|
PSYCH_QME_DEFAULT_DIAGNOSIS,
|
|
8502
8902
|
REPORT_BILL_STATUS_OPTIONS2 as REPORT_BILL_STATUS_OPTIONS,
|
|
8503
8903
|
ReportBillStatusDialog,
|
|
8904
|
+
RfaDraftForm,
|
|
8504
8905
|
SECOND_REVIEW_REASON_TEMPLATE,
|
|
8505
8906
|
SECOND_REVIEW_REASON_TEMPLATES,
|
|
8506
8907
|
SECOND_REVIEW_WINDOW_DAYS,
|
|
@@ -8529,6 +8930,7 @@ export {
|
|
|
8529
8930
|
createBillLifecycleClient2 as createBillLifecycleClient,
|
|
8530
8931
|
createBillStatusClient,
|
|
8531
8932
|
createBillingOperationsClient,
|
|
8933
|
+
dentalDraftChargeSummary,
|
|
8532
8934
|
ensureTrailingBillSubmissionLine,
|
|
8533
8935
|
ensureTrailingProcedureLine,
|
|
8534
8936
|
exactClaimsAdministratorMatch,
|
|
@@ -8539,8 +8941,10 @@ export {
|
|
|
8539
8941
|
mindBillAppearanceStyle,
|
|
8540
8942
|
mindBillThemePresets,
|
|
8541
8943
|
normalizeClaimsAdministratorName,
|
|
8944
|
+
normalizeRfaDraft,
|
|
8542
8945
|
organizationProfileOptions,
|
|
8543
8946
|
parseBillSubmissionDate,
|
|
8947
|
+
parseDentalCharge,
|
|
8544
8948
|
prepareBillSubmissionDocuments,
|
|
8545
8949
|
reportBillStatusContacts2 as reportBillStatusContacts,
|
|
8546
8950
|
resolveMindBillAppearance,
|
|
@@ -8550,6 +8954,8 @@ export {
|
|
|
8550
8954
|
useBillLifecycle,
|
|
8551
8955
|
useBillStatus,
|
|
8552
8956
|
validateBillSubmission,
|
|
8957
|
+
validateDentalDraftContent,
|
|
8958
|
+
validateRfaDraft,
|
|
8553
8959
|
visibleBillLifecycleActions
|
|
8554
8960
|
};
|
|
8555
8961
|
//# sourceMappingURL=index.js.map
|