@mindbill/angular 0.8.0 → 0.9.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 +14 -0
- package/fesm2022/mindbill-angular.mjs +343 -10
- package/fesm2022/mindbill-angular.mjs.map +1 -1
- package/package.json +2 -2
- package/types/mindbill-angular.d.ts +53 -3
package/README.md
CHANGED
|
@@ -126,3 +126,17 @@ The operations surfaces consume a normalized list of bills, so they can be used
|
|
|
126
126
|
`mindbill-status-aging-matrix` is the management view billers coming from legacy tools expect: one row per lifecycle status, one column per 0–30 / 31–60 / 61–90 / 91+ aging bucket, clickable counts with outstanding balances, and row/column totals. Every emitted cell carries `{ state, bucket, count, balance, bills }`, so a drill-down never needs a second query. Pin a custom lifecycle ordering with `[stateOrder]`; unknown states append alphabetically.
|
|
127
127
|
|
|
128
128
|
`summarizeMindBillDashboard`, `buildMindBillReportRows`, `buildMindBillReportCsv`, `buildMindBillStatusAgingMatrix`, and `buildMindBillStatusAgingCsv` are also exported for custom layouts and server-side reporting.
|
|
129
|
+
|
|
130
|
+
## Organization onboarding and billing settings
|
|
131
|
+
|
|
132
|
+
`mindbill-organization-onboarding` captures the practice identity, billing provider, locations, and W-9 once — saved straight to your MindBill organization through the browser session. Set `variant="settings"` for the compact edit-after-setup layout.
|
|
133
|
+
|
|
134
|
+
```html
|
|
135
|
+
<mindbill-organization-onboarding
|
|
136
|
+
sessionEndpoint="/api/mindbill/session"
|
|
137
|
+
[appearance]="appearance"
|
|
138
|
+
(completed)="enableBillingFeatures()"
|
|
139
|
+
/>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
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.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { signal, computed, EventEmitter, effect, Output, Input, Component, Directive, inject, ChangeDetectorRef, HostListener } from '@angular/core';
|
|
3
|
-
import { createBillLifecycleClient, createBillReferenceClient, createBillSubmissionClient } from '@mindbill/browser';
|
|
3
|
+
import { createBillLifecycleClient, createBillReferenceClient, createBillSubmissionClient, createOrganizationClient } from '@mindbill/browser';
|
|
4
4
|
import * as i1 from '@angular/common';
|
|
5
5
|
import { CommonModule } from '@angular/common';
|
|
6
6
|
import * as i2 from '@angular/forms';
|
|
@@ -275,7 +275,7 @@ function ensureTrailingProcedureLine(lines) {
|
|
|
275
275
|
: entered;
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
-
const THEME$
|
|
278
|
+
const THEME$4 = {
|
|
279
279
|
mindbill: { a: "#238dbd", ac: "#fff", bg: "#f3f8fa", s: "#fff", t: "#203743", m: "#657982", b: "#dbe6ea", r: "14px", cr: "8px", font: "Inter,system-ui,sans-serif" },
|
|
280
280
|
"qme-companion": { a: "#53b5dc", ac: "#173542", bg: "#f2f8fb", s: "#fff", t: "#1d3440", m: "#617783", b: "#d7e5eb", r: "12px", cr: "8px", font: "Inter,system-ui,sans-serif" },
|
|
281
281
|
"orange-bright": { a: "#f4510b", ac: "#fff", bg: "#fffaf6", s: "#fffefd", t: "#090f1f", m: "#626a73", b: "#e7e1da", r: "16px", cr: "10px", font: "Inter,system-ui,sans-serif" },
|
|
@@ -349,7 +349,7 @@ function buildMindBillReportCsv(rows) {
|
|
|
349
349
|
class DashboardBase {
|
|
350
350
|
appearance = { preset: "mindbill" };
|
|
351
351
|
get themeStyle() {
|
|
352
|
-
const base = THEME$
|
|
352
|
+
const base = THEME$4[this.appearance.preset ?? "mindbill"] ?? THEME$4["mindbill"];
|
|
353
353
|
return {
|
|
354
354
|
"--a": this.appearance.accentColor ?? base["a"], "--ac": this.appearance.accentTextColor ?? base["ac"],
|
|
355
355
|
"--bg": this.appearance.backgroundColor ?? base["bg"], "--s": this.appearance.surfaceColor ?? base["s"],
|
|
@@ -512,7 +512,7 @@ function buildMindBillStatusAgingCsv(bills, stateOrder) {
|
|
|
512
512
|
return [header, ...lines].map((line) => line.map(quote).join(",")).join("\n");
|
|
513
513
|
}
|
|
514
514
|
|
|
515
|
-
const THEME$
|
|
515
|
+
const THEME$3 = {
|
|
516
516
|
mindbill: { a: "#238dbd", ac: "#fff", bg: "#f3f8fa", s: "#fff", t: "#203743", m: "#657982", b: "#dbe6ea", r: "14px", cr: "8px", font: "Inter,system-ui,sans-serif" },
|
|
517
517
|
"qme-companion": { a: "#53b5dc", ac: "#173542", bg: "#f2f8fb", s: "#fff", t: "#1d3440", m: "#617783", b: "#d7e5eb", r: "12px", cr: "8px", font: "Inter,system-ui,sans-serif" },
|
|
518
518
|
"orange-bright": { a: "#f4510b", ac: "#fff", bg: "#fffaf6", s: "#fffefd", t: "#090f1f", m: "#626a73", b: "#e7e1da", r: "16px", cr: "10px", font: "Inter,system-ui,sans-serif" },
|
|
@@ -531,7 +531,7 @@ class MindBillStatusAgingMatrixComponent {
|
|
|
531
531
|
return buildMindBillStatusAgingMatrix(this.bills, this.stateOrder);
|
|
532
532
|
}
|
|
533
533
|
get themeStyle() {
|
|
534
|
-
const base = THEME$
|
|
534
|
+
const base = THEME$3[this.appearance.preset ?? "mindbill"] ?? THEME$3["mindbill"];
|
|
535
535
|
return {
|
|
536
536
|
"--a": this.appearance.accentColor ?? base["a"], "--ac": this.appearance.accentTextColor ?? base["ac"],
|
|
537
537
|
"--bg": this.appearance.backgroundColor ?? base["bg"], "--s": this.appearance.surfaceColor ?? base["s"],
|
|
@@ -968,7 +968,7 @@ const QUICK_DIAGNOSES = [
|
|
|
968
968
|
{ label: "Left knee", code: "M25.562", description: "Pain in left knee" },
|
|
969
969
|
{ label: "Right knee", code: "M25.561", description: "Pain in right knee" },
|
|
970
970
|
];
|
|
971
|
-
const THEME$
|
|
971
|
+
const THEME$2 = {
|
|
972
972
|
mindbill: { a: "#238dbd", ac: "#fff", bg: "#f3f8fa", s: "#fff", t: "#203743", m: "#657982", b: "#dbe6ea", r: "14px", cr: "8px", font: "Inter,system-ui,sans-serif" },
|
|
973
973
|
"qme-companion": { a: "#53b5dc", ac: "#173542", bg: "#f2f8fb", s: "#fff", t: "#1d3440", m: "#617783", b: "#d7e5eb", r: "12px", cr: "8px", font: "Inter,system-ui,sans-serif" },
|
|
974
974
|
"orange-bright": { a: "#f4510b", ac: "#fff", bg: "#fffaf6", s: "#fffefd", t: "#090f1f", m: "#626a73", b: "#e7e1da", r: "16px", cr: "10px", font: "Inter,system-ui,sans-serif" },
|
|
@@ -1041,7 +1041,7 @@ class MindBillBillSubmissionComponent {
|
|
|
1041
1041
|
this.workingAttachments = this.attachments.map((item) => ({ ...item }));
|
|
1042
1042
|
}
|
|
1043
1043
|
get themeStyle() {
|
|
1044
|
-
const base = THEME$
|
|
1044
|
+
const base = THEME$2[this.appearance.preset ?? "mindbill"] ?? THEME$2["mindbill"];
|
|
1045
1045
|
return {
|
|
1046
1046
|
"--a": this.appearance.accentColor ?? base["a"], "--ac": this.appearance.accentTextColor ?? base["ac"],
|
|
1047
1047
|
"--bg": this.appearance.backgroundColor ?? base["bg"], "--s": this.appearance.surfaceColor ?? base["s"],
|
|
@@ -1593,7 +1593,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
1593
1593
|
args: ["window:drop", ["$event"]]
|
|
1594
1594
|
}] } });
|
|
1595
1595
|
|
|
1596
|
-
const THEME = {
|
|
1596
|
+
const THEME$1 = {
|
|
1597
1597
|
mindbill: { a: "#238dbd", ac: "#fff", b: "#dbe6ea", r: "8px", font: "Inter,system-ui,sans-serif" },
|
|
1598
1598
|
"qme-companion": { a: "#53b5dc", ac: "#173542", b: "#d7e5eb", r: "8px", font: "Inter,system-ui,sans-serif" },
|
|
1599
1599
|
"orange-bright": { a: "#f4510b", ac: "#fff", b: "#e7e1da", r: "10px", font: "Inter,system-ui,sans-serif" },
|
|
@@ -1611,7 +1611,7 @@ class MindBillBillingManagementButtonComponent {
|
|
|
1611
1611
|
errorMessage = "";
|
|
1612
1612
|
changeDetector = inject(ChangeDetectorRef);
|
|
1613
1613
|
get themeStyle() {
|
|
1614
|
-
const base = THEME[this.appearance.preset ?? "mindbill"] ?? THEME["mindbill"];
|
|
1614
|
+
const base = THEME$1[this.appearance.preset ?? "mindbill"] ?? THEME$1["mindbill"];
|
|
1615
1615
|
return {
|
|
1616
1616
|
"--a": this.appearance.accentColor ?? base["a"],
|
|
1617
1617
|
"--ac": this.appearance.accentTextColor ?? base["ac"],
|
|
@@ -1700,9 +1700,342 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
1700
1700
|
type: Output
|
|
1701
1701
|
}] } });
|
|
1702
1702
|
|
|
1703
|
+
// Embeddable organization onboarding (INC-1470): practice identity, billing
|
|
1704
|
+
// provider, locations, and the W-9 captured once and saved straight to
|
|
1705
|
+
// MindBill through the partner browser session (organization:manage
|
|
1706
|
+
// permission). variant="settings" stacks every section for edit-after-setup.
|
|
1707
|
+
const THEME = {
|
|
1708
|
+
mindbill: { a: "#238dbd", ac: "#fff", bg: "#f3f8fa", s: "#fff", t: "#203743", m: "#657982", b: "#dbe6ea", r: "14px", cr: "8px", font: "Inter,system-ui,sans-serif" },
|
|
1709
|
+
"qme-companion": { a: "#53b5dc", ac: "#173542", bg: "#f2f8fb", s: "#fff", t: "#1d3440", m: "#617783", b: "#d7e5eb", r: "12px", cr: "8px", font: "Inter,system-ui,sans-serif" },
|
|
1710
|
+
"orange-bright": { a: "#f4510b", ac: "#fff", bg: "#fffaf6", s: "#fffefd", t: "#090f1f", m: "#626a73", b: "#e7e1da", r: "16px", cr: "10px", font: "Inter,system-ui,sans-serif" },
|
|
1711
|
+
"clinical-blue": { a: "#1677ff", ac: "#fff", bg: "#fff", s: "#fff", t: "#1f2d3d", m: "#66788a", b: "#d9e2ec", r: "8px", cr: "6px", font: "Inter,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif" },
|
|
1712
|
+
};
|
|
1713
|
+
async function fileToBase64(file) {
|
|
1714
|
+
const buffer = new Uint8Array(await file.arrayBuffer());
|
|
1715
|
+
let binary = "";
|
|
1716
|
+
for (const byte of buffer)
|
|
1717
|
+
binary += String.fromCharCode(byte);
|
|
1718
|
+
return btoa(binary);
|
|
1719
|
+
}
|
|
1720
|
+
class MindBillOrganizationOnboardingComponent {
|
|
1721
|
+
appearance = { preset: "mindbill" };
|
|
1722
|
+
sessionEndpoint = "/api/mindbill/session";
|
|
1723
|
+
getSession;
|
|
1724
|
+
apiBaseUrl;
|
|
1725
|
+
heading = "Billing setup";
|
|
1726
|
+
description = "Practice identity, locations, and the W-9 — saved once, used on every bill.";
|
|
1727
|
+
variant = "onboarding";
|
|
1728
|
+
saved$ = new EventEmitter();
|
|
1729
|
+
completed = new EventEmitter();
|
|
1730
|
+
organizationError = new EventEmitter();
|
|
1731
|
+
steps = [
|
|
1732
|
+
{ id: "practice", label: "Practice & billing" },
|
|
1733
|
+
{ id: "locations", label: "Locations" },
|
|
1734
|
+
{ id: "w9", label: "W-9" },
|
|
1735
|
+
{ id: "review", label: "Review" },
|
|
1736
|
+
];
|
|
1737
|
+
profile = null;
|
|
1738
|
+
loadError = "";
|
|
1739
|
+
errorMessage = "";
|
|
1740
|
+
step = "practice";
|
|
1741
|
+
saving = false;
|
|
1742
|
+
saved = {};
|
|
1743
|
+
identity = { name: "", legalName: "", taxId: "", npi: "", phone: "", email: "" };
|
|
1744
|
+
provider = { name: "", taxId: "", npi: "", billType: "Professional", phone: "", billingStreet: "", billingCity: "", billingState: "", billingZip: "" };
|
|
1745
|
+
locations = [{ name: "", street: "", city: "", state: "", zip: "", isPrimary: false }];
|
|
1746
|
+
w9File = null;
|
|
1747
|
+
changeDetector = inject(ChangeDetectorRef);
|
|
1748
|
+
completedFired = false;
|
|
1749
|
+
client;
|
|
1750
|
+
ngOnInit() {
|
|
1751
|
+
this.client = createOrganizationClient({
|
|
1752
|
+
sessionEndpoint: this.sessionEndpoint,
|
|
1753
|
+
...(this.getSession ? { getSession: this.getSession } : {}),
|
|
1754
|
+
...(this.apiBaseUrl ? { apiBaseUrl: this.apiBaseUrl } : {}),
|
|
1755
|
+
});
|
|
1756
|
+
this.client.getOrganization()
|
|
1757
|
+
.then((profile) => { this.loadError = ""; this.adoptProfile(profile); })
|
|
1758
|
+
.catch((error) => {
|
|
1759
|
+
const failure = error instanceof Error ? error : new Error("The organization could not be loaded.");
|
|
1760
|
+
this.loadError = failure.message;
|
|
1761
|
+
this.organizationError.emit(failure);
|
|
1762
|
+
})
|
|
1763
|
+
.finally(() => this.changeDetector.markForCheck());
|
|
1764
|
+
}
|
|
1765
|
+
get themeStyle() {
|
|
1766
|
+
const base = THEME[this.appearance.preset ?? "mindbill"] ?? THEME["mindbill"];
|
|
1767
|
+
return {
|
|
1768
|
+
"--a": this.appearance.accentColor ?? base["a"], "--ac": this.appearance.accentTextColor ?? base["ac"],
|
|
1769
|
+
"--bg": this.appearance.backgroundColor ?? base["bg"], "--s": this.appearance.surfaceColor ?? base["s"],
|
|
1770
|
+
"--t": this.appearance.textColor ?? base["t"], "--m": this.appearance.mutedColor ?? base["m"],
|
|
1771
|
+
"--b": this.appearance.borderColor ?? base["b"], "--r": this.appearance.borderRadius ?? base["r"],
|
|
1772
|
+
"--cr": this.appearance.controlRadius ?? base["cr"], "--font": this.appearance.fontFamily ?? base["font"],
|
|
1773
|
+
};
|
|
1774
|
+
}
|
|
1775
|
+
adoptProfile(profile) {
|
|
1776
|
+
this.profile = profile;
|
|
1777
|
+
this.identity = {
|
|
1778
|
+
name: profile.practiceIdentity.name ?? this.identity.name,
|
|
1779
|
+
legalName: profile.practiceIdentity.legalName ?? this.identity.legalName,
|
|
1780
|
+
taxId: profile.practiceIdentity.taxId ?? this.identity.taxId,
|
|
1781
|
+
npi: profile.practiceIdentity.npi ?? this.identity.npi,
|
|
1782
|
+
phone: profile.practiceIdentity.phone ?? this.identity.phone,
|
|
1783
|
+
email: profile.practiceIdentity.email ?? this.identity.email,
|
|
1784
|
+
};
|
|
1785
|
+
if (profile.billingProviders[0])
|
|
1786
|
+
this.provider = { billType: "Professional", ...profile.billingProviders[0] };
|
|
1787
|
+
if (profile.locations.length)
|
|
1788
|
+
this.locations = profile.locations.map((item) => ({ ...item }));
|
|
1789
|
+
if (profile.onboarding.complete && !this.completedFired) {
|
|
1790
|
+
this.completedFired = true;
|
|
1791
|
+
this.completed.emit(profile);
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
addLocation() {
|
|
1795
|
+
this.locations.push({ name: "", street: "", city: "", state: "", zip: "", isPrimary: false });
|
|
1796
|
+
}
|
|
1797
|
+
setPrimary(index) {
|
|
1798
|
+
this.locations = this.locations.map((item, itemIndex) => ({ ...item, isPrimary: itemIndex === index }));
|
|
1799
|
+
}
|
|
1800
|
+
w9Selected(event) {
|
|
1801
|
+
this.w9File = event.target.files?.[0] ?? null;
|
|
1802
|
+
}
|
|
1803
|
+
async run(work, stepId) {
|
|
1804
|
+
this.saving = true;
|
|
1805
|
+
this.errorMessage = "";
|
|
1806
|
+
try {
|
|
1807
|
+
const profile = await work();
|
|
1808
|
+
this.adoptProfile(profile);
|
|
1809
|
+
this.saved[stepId] = true;
|
|
1810
|
+
this.saved$.emit(profile);
|
|
1811
|
+
if (this.variant === "onboarding") {
|
|
1812
|
+
const order = ["practice", "locations", "w9", "review"];
|
|
1813
|
+
const next = order[order.indexOf(stepId) + 1];
|
|
1814
|
+
if (next)
|
|
1815
|
+
this.step = next;
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
catch (error) {
|
|
1819
|
+
const failure = error instanceof Error ? error : new Error("Saving failed.");
|
|
1820
|
+
this.errorMessage = failure.message;
|
|
1821
|
+
this.organizationError.emit(failure);
|
|
1822
|
+
}
|
|
1823
|
+
finally {
|
|
1824
|
+
this.saving = false;
|
|
1825
|
+
this.changeDetector.markForCheck();
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
savePractice() {
|
|
1829
|
+
void this.run(() => this.client.saveBillingProfile({
|
|
1830
|
+
practiceIdentity: this.identity,
|
|
1831
|
+
...(this.provider.name.trim() || this.provider.taxId.trim() || this.provider.npi.trim()
|
|
1832
|
+
? { billingProviders: [this.provider] }
|
|
1833
|
+
: {}),
|
|
1834
|
+
}), "practice");
|
|
1835
|
+
}
|
|
1836
|
+
saveLocations() {
|
|
1837
|
+
void this.run(() => this.client.saveLocations(this.locations.filter((item) => item.name.trim() || item.street.trim())), "locations");
|
|
1838
|
+
}
|
|
1839
|
+
saveW9() {
|
|
1840
|
+
void this.run(async () => {
|
|
1841
|
+
if (!this.w9File)
|
|
1842
|
+
throw new Error("Choose the practice W-9 PDF first.");
|
|
1843
|
+
return this.client.saveW9({ filename: this.w9File.name, contentBase64: await fileToBase64(this.w9File) });
|
|
1844
|
+
}, "w9");
|
|
1845
|
+
}
|
|
1846
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: MindBillOrganizationOnboardingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1847
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: MindBillOrganizationOnboardingComponent, isStandalone: true, selector: "mindbill-organization-onboarding", inputs: { appearance: "appearance", sessionEndpoint: "sessionEndpoint", getSession: "getSession", apiBaseUrl: "apiBaseUrl", heading: "heading", description: "description", variant: "variant" }, outputs: { saved$: "saved$", completed: "completed", organizationError: "organizationError" }, ngImport: i0, template: `
|
|
1848
|
+
<section class="mbob" [ngStyle]="themeStyle">
|
|
1849
|
+
<div class="card">
|
|
1850
|
+
<header><h2>{{ heading }}</h2><p>{{ description }}</p></header>
|
|
1851
|
+
@if (loadError) { <div class="error" role="alert">{{ loadError }}</div> }
|
|
1852
|
+
|
|
1853
|
+
@if (variant === 'onboarding') {
|
|
1854
|
+
<div class="steps">
|
|
1855
|
+
@for (item of steps; track item.id) {
|
|
1856
|
+
<button type="button" [class.active]="step === item.id" [class.done]="saved[item.id]" (click)="step = item.id"><i>{{ saved[item.id] ? '✓' : '' }}</i>{{ item.label }}</button>
|
|
1857
|
+
}
|
|
1858
|
+
</div>
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
@if (variant === 'settings' || step === 'practice') {
|
|
1862
|
+
<div class="grid">
|
|
1863
|
+
<label>Practice name<input [(ngModel)]="identity.name" [disabled]="saving"></label>
|
|
1864
|
+
<label>Legal name<input [(ngModel)]="identity.legalName" [disabled]="saving"></label>
|
|
1865
|
+
<label>Tax ID (EIN)<input [(ngModel)]="identity.taxId" placeholder="94-1234567" [disabled]="saving"></label>
|
|
1866
|
+
<label>Group NPI<input [(ngModel)]="identity.npi" [disabled]="saving"></label>
|
|
1867
|
+
<label>Phone<input [(ngModel)]="identity.phone" [disabled]="saving"></label>
|
|
1868
|
+
<label>Email<input [(ngModel)]="identity.email" [disabled]="saving"></label>
|
|
1869
|
+
</div>
|
|
1870
|
+
<h3>Billing provider (pay-to)</h3>
|
|
1871
|
+
<div class="grid">
|
|
1872
|
+
<label>Billing provider name<input [(ngModel)]="provider.name" [disabled]="saving"></label>
|
|
1873
|
+
<label>Billing tax ID<input [(ngModel)]="provider.taxId" [disabled]="saving"></label>
|
|
1874
|
+
<label>Billing NPI<input [(ngModel)]="provider.npi" [disabled]="saving"></label>
|
|
1875
|
+
<label>Billing phone<input [(ngModel)]="provider.phone" [disabled]="saving"></label>
|
|
1876
|
+
<label class="span">Street<input [(ngModel)]="provider.billingStreet" [disabled]="saving"></label>
|
|
1877
|
+
<label>City<input [(ngModel)]="provider.billingCity" [disabled]="saving"></label>
|
|
1878
|
+
<label>State<input [(ngModel)]="provider.billingState" maxlength="2" [disabled]="saving"></label>
|
|
1879
|
+
<label>ZIP<input [(ngModel)]="provider.billingZip" [disabled]="saving"></label>
|
|
1880
|
+
</div>
|
|
1881
|
+
<div class="actions"><span>{{ saved['practice'] ? 'Saved to MindBill.' : 'Saved once, used on every bill.' }}</span><button type="button" class="save" [disabled]="saving" (click)="savePractice()">{{ saving ? 'Saving…' : 'Save practice' }}</button></div>
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
@if (variant === 'settings') { <h3>Locations</h3> }
|
|
1885
|
+
@if (variant === 'settings' || step === 'locations') {
|
|
1886
|
+
@for (location of locations; track $index; let index = $index) {
|
|
1887
|
+
<div class="loc">
|
|
1888
|
+
<label>Name<input [(ngModel)]="location.name" [disabled]="saving"></label>
|
|
1889
|
+
<label>Street<input [(ngModel)]="location.street" [disabled]="saving"></label>
|
|
1890
|
+
<label>City<input [(ngModel)]="location.city" [disabled]="saving"></label>
|
|
1891
|
+
<label>State<input [(ngModel)]="location.state" maxlength="2" [disabled]="saving"></label>
|
|
1892
|
+
<label>ZIP<input [(ngModel)]="location.zip" [disabled]="saving"></label>
|
|
1893
|
+
<label class="primary"><input type="radio" name="mbob-primary" [checked]="location.isPrimary === true" (change)="setPrimary(index)"> Primary</label>
|
|
1894
|
+
<button type="button" class="remove" aria-label="Remove location" (click)="locations.splice(index, 1)">×</button>
|
|
1895
|
+
</div>
|
|
1896
|
+
}
|
|
1897
|
+
<button type="button" class="add" (click)="addLocation()">+ Add location</button>
|
|
1898
|
+
<div class="actions"><span>{{ saved['locations'] ? 'Saved to MindBill.' : 'Where evaluations happen.' }}</span><button type="button" class="save" [disabled]="saving" (click)="saveLocations()">{{ saving ? 'Saving…' : 'Save locations' }}</button></div>
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
@if (variant === 'settings') { <h3>Practice W-9</h3> }
|
|
1902
|
+
@if (variant === 'settings' || step === 'w9') {
|
|
1903
|
+
@if (profile?.w9; as w9) {
|
|
1904
|
+
<div class="w9-current"><div><strong>{{ w9.filename }}</strong><span> · added {{ w9.addDate }}</span></div><span>Rides on every bill automatically</span></div>
|
|
1905
|
+
}
|
|
1906
|
+
<label class="drop">
|
|
1907
|
+
<input type="file" accept="application/pdf,.pdf" (change)="w9Selected($event)">
|
|
1908
|
+
<strong>{{ w9File ? w9File.name : (profile?.w9 ? 'Replace the W-9 PDF' : 'Drop the practice W-9 PDF here, or click to choose') }}</strong>
|
|
1909
|
+
<span>The W-9 is auto-attached to every submission.</span>
|
|
1910
|
+
</label>
|
|
1911
|
+
<div class="actions"><span>{{ saved['w9'] ? 'Saved to MindBill.' : '' }}</span><button type="button" class="save" [disabled]="saving || !w9File" (click)="saveW9()">{{ saving ? 'Uploading…' : 'Save W-9' }}</button></div>
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1914
|
+
@if (variant === 'settings') { <h3>Checklist</h3> }
|
|
1915
|
+
@if (variant === 'settings' || step === 'review') {
|
|
1916
|
+
<div class="check">
|
|
1917
|
+
@for (item of profile?.onboarding?.checklist ?? []; track item.id) {
|
|
1918
|
+
<div class="check-item" [class.complete]="item.complete"><i>{{ item.complete ? '✓' : '•' }}</i><span>{{ item.label }}</span></div>
|
|
1919
|
+
}
|
|
1920
|
+
@if (profile?.onboarding?.complete) { <p class="done-copy">Billing setup is complete — bills submitted from your product carry this profile automatically.</p> }
|
|
1921
|
+
@else { <p class="muted">Items above complete themselves as each section is saved.</p> }
|
|
1922
|
+
</div>
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
@if (errorMessage) { <div class="error" role="alert">{{ errorMessage }}</div> }
|
|
1926
|
+
</div>
|
|
1927
|
+
</section>
|
|
1928
|
+
`, isInline: true, styles: [":host{display:block}.mbob{font-family:var(--font);color:var(--t);font-size:15px}.mbob *{box-sizing:border-box}.card{border:1px solid var(--b);border-radius:var(--r);background:var(--s);padding:24px}header{margin-bottom:18px}header h2{margin:0;font-size:22px}header p{margin:5px 0 0;color:var(--m)}.steps{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:20px}.steps button{display:inline-flex;align-items:center;gap:7px;border:1px solid var(--b);border-radius:999px;background:var(--s);padding:7px 13px;color:var(--m);font:inherit;font-size:13px;font-weight:700;cursor:pointer}.steps button.active{border-color:var(--a);color:var(--a);background:color-mix(in srgb,var(--a) 8%,var(--s))}.steps button.done i{color:#159447;font-style:normal}.grid{display:grid;grid-template-columns:1fr 1fr;gap:16px 22px}.span{grid-column:1/-1}label{display:grid;gap:7px;font-size:13px;font-weight:720}input:not([type=radio]):not([type=file]){width:100%;min-height:44px;border:1px solid var(--b);border-radius:var(--cr);background:var(--s);padding:10px 12px;color:var(--t);font:inherit;font-weight:450}input:focus{outline:3px solid color-mix(in srgb,var(--a) 22%,transparent);border-color:var(--a)}h3{margin:20px 0 12px;padding-top:16px;border-top:1px solid var(--b);font-size:15px}.loc{display:grid;grid-template-columns:1.2fr 1.4fr .9fr .4fr .6fr auto auto;gap:10px;align-items:end;border-top:1px solid color-mix(in srgb,var(--b) 60%,transparent);padding:12px 0}.loc:first-of-type{border-top:0}.primary{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--m);white-space:nowrap;padding-bottom:12px}.remove{border:0;background:transparent;color:var(--m);font-size:20px;cursor:pointer;padding-bottom:8px}.add{border:1px solid var(--b);border-radius:var(--cr);background:var(--s);padding:10px 14px;color:var(--t);font:inherit;font-weight:700;cursor:pointer}.drop{display:grid;place-content:center;gap:6px;min-height:150px;border:2px dashed color-mix(in srgb,var(--m) 55%,transparent);border-radius:var(--cr);text-align:center;cursor:pointer;position:relative}.drop input{position:absolute;inset:0;opacity:0;cursor:pointer}.drop span{color:var(--m);font-size:13px}.w9-current{display:flex;align-items:center;justify-content:space-between;gap:12px;border:1px solid #9fd6b4;border-radius:var(--cr);background:#f3fcf6;padding:12px 14px;margin-bottom:14px}.w9-current span{color:var(--m);font-size:13px}.check{display:grid;gap:9px}.check-item{display:flex;align-items:center;gap:10px;border:1px solid var(--b);border-radius:var(--cr);padding:11px 14px}.check-item.complete{border-color:#9fd6b4;background:#f3fcf6}.check-item i{font-style:normal}.actions{display:flex;justify-content:space-between;align-items:center;gap:14px;margin-top:20px}.actions>span{color:var(--m)}.save{min-width:170px;border:0;border-radius:var(--cr);background:var(--a);color:var(--ac);padding:12px 22px;font:inherit;font-weight:780;cursor:pointer}.save:disabled{opacity:.6;cursor:wait}.error{margin-top:14px;border-left:4px solid #c83c3c;border-radius:var(--cr);background:color-mix(in srgb,#c83c3c 8%,var(--s));padding:12px 14px;color:#c83c3c}.done-copy{color:#159447;font-weight:750}.muted{color:var(--m)}@media(max-width:820px){.grid{grid-template-columns:1fr}.span{grid-column:auto}.loc{grid-template-columns:1fr 1fr;align-items:center}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
|
|
1929
|
+
}
|
|
1930
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: MindBillOrganizationOnboardingComponent, decorators: [{
|
|
1931
|
+
type: Component,
|
|
1932
|
+
args: [{ selector: "mindbill-organization-onboarding", standalone: true, imports: [CommonModule, FormsModule], template: `
|
|
1933
|
+
<section class="mbob" [ngStyle]="themeStyle">
|
|
1934
|
+
<div class="card">
|
|
1935
|
+
<header><h2>{{ heading }}</h2><p>{{ description }}</p></header>
|
|
1936
|
+
@if (loadError) { <div class="error" role="alert">{{ loadError }}</div> }
|
|
1937
|
+
|
|
1938
|
+
@if (variant === 'onboarding') {
|
|
1939
|
+
<div class="steps">
|
|
1940
|
+
@for (item of steps; track item.id) {
|
|
1941
|
+
<button type="button" [class.active]="step === item.id" [class.done]="saved[item.id]" (click)="step = item.id"><i>{{ saved[item.id] ? '✓' : '' }}</i>{{ item.label }}</button>
|
|
1942
|
+
}
|
|
1943
|
+
</div>
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
@if (variant === 'settings' || step === 'practice') {
|
|
1947
|
+
<div class="grid">
|
|
1948
|
+
<label>Practice name<input [(ngModel)]="identity.name" [disabled]="saving"></label>
|
|
1949
|
+
<label>Legal name<input [(ngModel)]="identity.legalName" [disabled]="saving"></label>
|
|
1950
|
+
<label>Tax ID (EIN)<input [(ngModel)]="identity.taxId" placeholder="94-1234567" [disabled]="saving"></label>
|
|
1951
|
+
<label>Group NPI<input [(ngModel)]="identity.npi" [disabled]="saving"></label>
|
|
1952
|
+
<label>Phone<input [(ngModel)]="identity.phone" [disabled]="saving"></label>
|
|
1953
|
+
<label>Email<input [(ngModel)]="identity.email" [disabled]="saving"></label>
|
|
1954
|
+
</div>
|
|
1955
|
+
<h3>Billing provider (pay-to)</h3>
|
|
1956
|
+
<div class="grid">
|
|
1957
|
+
<label>Billing provider name<input [(ngModel)]="provider.name" [disabled]="saving"></label>
|
|
1958
|
+
<label>Billing tax ID<input [(ngModel)]="provider.taxId" [disabled]="saving"></label>
|
|
1959
|
+
<label>Billing NPI<input [(ngModel)]="provider.npi" [disabled]="saving"></label>
|
|
1960
|
+
<label>Billing phone<input [(ngModel)]="provider.phone" [disabled]="saving"></label>
|
|
1961
|
+
<label class="span">Street<input [(ngModel)]="provider.billingStreet" [disabled]="saving"></label>
|
|
1962
|
+
<label>City<input [(ngModel)]="provider.billingCity" [disabled]="saving"></label>
|
|
1963
|
+
<label>State<input [(ngModel)]="provider.billingState" maxlength="2" [disabled]="saving"></label>
|
|
1964
|
+
<label>ZIP<input [(ngModel)]="provider.billingZip" [disabled]="saving"></label>
|
|
1965
|
+
</div>
|
|
1966
|
+
<div class="actions"><span>{{ saved['practice'] ? 'Saved to MindBill.' : 'Saved once, used on every bill.' }}</span><button type="button" class="save" [disabled]="saving" (click)="savePractice()">{{ saving ? 'Saving…' : 'Save practice' }}</button></div>
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1969
|
+
@if (variant === 'settings') { <h3>Locations</h3> }
|
|
1970
|
+
@if (variant === 'settings' || step === 'locations') {
|
|
1971
|
+
@for (location of locations; track $index; let index = $index) {
|
|
1972
|
+
<div class="loc">
|
|
1973
|
+
<label>Name<input [(ngModel)]="location.name" [disabled]="saving"></label>
|
|
1974
|
+
<label>Street<input [(ngModel)]="location.street" [disabled]="saving"></label>
|
|
1975
|
+
<label>City<input [(ngModel)]="location.city" [disabled]="saving"></label>
|
|
1976
|
+
<label>State<input [(ngModel)]="location.state" maxlength="2" [disabled]="saving"></label>
|
|
1977
|
+
<label>ZIP<input [(ngModel)]="location.zip" [disabled]="saving"></label>
|
|
1978
|
+
<label class="primary"><input type="radio" name="mbob-primary" [checked]="location.isPrimary === true" (change)="setPrimary(index)"> Primary</label>
|
|
1979
|
+
<button type="button" class="remove" aria-label="Remove location" (click)="locations.splice(index, 1)">×</button>
|
|
1980
|
+
</div>
|
|
1981
|
+
}
|
|
1982
|
+
<button type="button" class="add" (click)="addLocation()">+ Add location</button>
|
|
1983
|
+
<div class="actions"><span>{{ saved['locations'] ? 'Saved to MindBill.' : 'Where evaluations happen.' }}</span><button type="button" class="save" [disabled]="saving" (click)="saveLocations()">{{ saving ? 'Saving…' : 'Save locations' }}</button></div>
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
@if (variant === 'settings') { <h3>Practice W-9</h3> }
|
|
1987
|
+
@if (variant === 'settings' || step === 'w9') {
|
|
1988
|
+
@if (profile?.w9; as w9) {
|
|
1989
|
+
<div class="w9-current"><div><strong>{{ w9.filename }}</strong><span> · added {{ w9.addDate }}</span></div><span>Rides on every bill automatically</span></div>
|
|
1990
|
+
}
|
|
1991
|
+
<label class="drop">
|
|
1992
|
+
<input type="file" accept="application/pdf,.pdf" (change)="w9Selected($event)">
|
|
1993
|
+
<strong>{{ w9File ? w9File.name : (profile?.w9 ? 'Replace the W-9 PDF' : 'Drop the practice W-9 PDF here, or click to choose') }}</strong>
|
|
1994
|
+
<span>The W-9 is auto-attached to every submission.</span>
|
|
1995
|
+
</label>
|
|
1996
|
+
<div class="actions"><span>{{ saved['w9'] ? 'Saved to MindBill.' : '' }}</span><button type="button" class="save" [disabled]="saving || !w9File" (click)="saveW9()">{{ saving ? 'Uploading…' : 'Save W-9' }}</button></div>
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
@if (variant === 'settings') { <h3>Checklist</h3> }
|
|
2000
|
+
@if (variant === 'settings' || step === 'review') {
|
|
2001
|
+
<div class="check">
|
|
2002
|
+
@for (item of profile?.onboarding?.checklist ?? []; track item.id) {
|
|
2003
|
+
<div class="check-item" [class.complete]="item.complete"><i>{{ item.complete ? '✓' : '•' }}</i><span>{{ item.label }}</span></div>
|
|
2004
|
+
}
|
|
2005
|
+
@if (profile?.onboarding?.complete) { <p class="done-copy">Billing setup is complete — bills submitted from your product carry this profile automatically.</p> }
|
|
2006
|
+
@else { <p class="muted">Items above complete themselves as each section is saved.</p> }
|
|
2007
|
+
</div>
|
|
2008
|
+
}
|
|
2009
|
+
|
|
2010
|
+
@if (errorMessage) { <div class="error" role="alert">{{ errorMessage }}</div> }
|
|
2011
|
+
</div>
|
|
2012
|
+
</section>
|
|
2013
|
+
`, styles: [":host{display:block}.mbob{font-family:var(--font);color:var(--t);font-size:15px}.mbob *{box-sizing:border-box}.card{border:1px solid var(--b);border-radius:var(--r);background:var(--s);padding:24px}header{margin-bottom:18px}header h2{margin:0;font-size:22px}header p{margin:5px 0 0;color:var(--m)}.steps{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:20px}.steps button{display:inline-flex;align-items:center;gap:7px;border:1px solid var(--b);border-radius:999px;background:var(--s);padding:7px 13px;color:var(--m);font:inherit;font-size:13px;font-weight:700;cursor:pointer}.steps button.active{border-color:var(--a);color:var(--a);background:color-mix(in srgb,var(--a) 8%,var(--s))}.steps button.done i{color:#159447;font-style:normal}.grid{display:grid;grid-template-columns:1fr 1fr;gap:16px 22px}.span{grid-column:1/-1}label{display:grid;gap:7px;font-size:13px;font-weight:720}input:not([type=radio]):not([type=file]){width:100%;min-height:44px;border:1px solid var(--b);border-radius:var(--cr);background:var(--s);padding:10px 12px;color:var(--t);font:inherit;font-weight:450}input:focus{outline:3px solid color-mix(in srgb,var(--a) 22%,transparent);border-color:var(--a)}h3{margin:20px 0 12px;padding-top:16px;border-top:1px solid var(--b);font-size:15px}.loc{display:grid;grid-template-columns:1.2fr 1.4fr .9fr .4fr .6fr auto auto;gap:10px;align-items:end;border-top:1px solid color-mix(in srgb,var(--b) 60%,transparent);padding:12px 0}.loc:first-of-type{border-top:0}.primary{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--m);white-space:nowrap;padding-bottom:12px}.remove{border:0;background:transparent;color:var(--m);font-size:20px;cursor:pointer;padding-bottom:8px}.add{border:1px solid var(--b);border-radius:var(--cr);background:var(--s);padding:10px 14px;color:var(--t);font:inherit;font-weight:700;cursor:pointer}.drop{display:grid;place-content:center;gap:6px;min-height:150px;border:2px dashed color-mix(in srgb,var(--m) 55%,transparent);border-radius:var(--cr);text-align:center;cursor:pointer;position:relative}.drop input{position:absolute;inset:0;opacity:0;cursor:pointer}.drop span{color:var(--m);font-size:13px}.w9-current{display:flex;align-items:center;justify-content:space-between;gap:12px;border:1px solid #9fd6b4;border-radius:var(--cr);background:#f3fcf6;padding:12px 14px;margin-bottom:14px}.w9-current span{color:var(--m);font-size:13px}.check{display:grid;gap:9px}.check-item{display:flex;align-items:center;gap:10px;border:1px solid var(--b);border-radius:var(--cr);padding:11px 14px}.check-item.complete{border-color:#9fd6b4;background:#f3fcf6}.check-item i{font-style:normal}.actions{display:flex;justify-content:space-between;align-items:center;gap:14px;margin-top:20px}.actions>span{color:var(--m)}.save{min-width:170px;border:0;border-radius:var(--cr);background:var(--a);color:var(--ac);padding:12px 22px;font:inherit;font-weight:780;cursor:pointer}.save:disabled{opacity:.6;cursor:wait}.error{margin-top:14px;border-left:4px solid #c83c3c;border-radius:var(--cr);background:color-mix(in srgb,#c83c3c 8%,var(--s));padding:12px 14px;color:#c83c3c}.done-copy{color:#159447;font-weight:750}.muted{color:var(--m)}@media(max-width:820px){.grid{grid-template-columns:1fr}.span{grid-column:auto}.loc{grid-template-columns:1fr 1fr;align-items:center}}\n"] }]
|
|
2014
|
+
}], propDecorators: { appearance: [{
|
|
2015
|
+
type: Input
|
|
2016
|
+
}], sessionEndpoint: [{
|
|
2017
|
+
type: Input
|
|
2018
|
+
}], getSession: [{
|
|
2019
|
+
type: Input
|
|
2020
|
+
}], apiBaseUrl: [{
|
|
2021
|
+
type: Input
|
|
2022
|
+
}], heading: [{
|
|
2023
|
+
type: Input
|
|
2024
|
+
}], description: [{
|
|
2025
|
+
type: Input
|
|
2026
|
+
}], variant: [{
|
|
2027
|
+
type: Input
|
|
2028
|
+
}], saved$: [{
|
|
2029
|
+
type: Output
|
|
2030
|
+
}], completed: [{
|
|
2031
|
+
type: Output
|
|
2032
|
+
}], organizationError: [{
|
|
2033
|
+
type: Output
|
|
2034
|
+
}] } });
|
|
2035
|
+
|
|
1703
2036
|
/**
|
|
1704
2037
|
* Generated bundle index. Do not edit.
|
|
1705
2038
|
*/
|
|
1706
2039
|
|
|
1707
|
-
export { MINDBILL_STATUS_AGING_BUCKETS, MindBillBillAgingSummaryComponent, MindBillBillLifecycleComponent, MindBillBillListComponent, MindBillBillSubmissionComponent, MindBillBillingDashboardComponent, MindBillBillingManagementButtonComponent, MindBillBillingReportComponent, MindBillComboBoxComponent, MindBillDateInputComponent, MindBillLifecycleStore, MindBillStatusAgingMatrixComponent, buildMindBillReportCsv, buildMindBillReportRows, buildMindBillStatusAgingCsv, buildMindBillStatusAgingMatrix, ensureTrailingProcedureLine, formatMindBillSubmissionDate, isEmptyProcedureLine, mindBillAgingBucket, mindBillAgingDays, mindBillCustomProcedureOption, parseMindBillSubmissionDate, summarizeMindBillDashboard };
|
|
2040
|
+
export { MINDBILL_STATUS_AGING_BUCKETS, MindBillBillAgingSummaryComponent, MindBillBillLifecycleComponent, MindBillBillListComponent, MindBillBillSubmissionComponent, MindBillBillingDashboardComponent, MindBillBillingManagementButtonComponent, MindBillBillingReportComponent, MindBillComboBoxComponent, MindBillDateInputComponent, MindBillLifecycleStore, MindBillOrganizationOnboardingComponent, MindBillStatusAgingMatrixComponent, buildMindBillReportCsv, buildMindBillReportRows, buildMindBillStatusAgingCsv, buildMindBillStatusAgingMatrix, ensureTrailingProcedureLine, formatMindBillSubmissionDate, isEmptyProcedureLine, mindBillAgingBucket, mindBillAgingDays, mindBillCustomProcedureOption, parseMindBillSubmissionDate, summarizeMindBillDashboard };
|
|
1708
2041
|
//# sourceMappingURL=mindbill-angular.mjs.map
|