@mindbill/angular 0.2.1 → 0.4.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 CHANGED
@@ -6,7 +6,7 @@ Native Angular billing UI with built-in session renewal and lifecycle API calls.
6
6
  npm install @mindbill/angular @mindbill/node
7
7
  ```
8
8
 
9
- Import the standalone component and give it the one value your application keeps: the bill ID.
9
+ Import the standalone component. Pass either an existing bill ID or the known values for a new bill.
10
10
 
11
11
  ```ts
12
12
  import { Component } from "@angular/core";
@@ -18,9 +18,10 @@ import { MindBillBillLifecycleComponent } from "@mindbill/angular";
18
18
  imports: [MindBillBillLifecycleComponent],
19
19
  template: `
20
20
  <mindbill-bill-lifecycle
21
- [billId]="billId"
22
- sessionEndpoint="/api/mindbill/bill-session"
21
+ [create]="knownBillValues"
22
+ sessionEndpoint="/api/mindbill/session"
23
23
  [appearance]="{ preset: 'clinical-blue' }"
24
+ (billCreated)="rememberBill($event)"
24
25
  (billIdChange)="billId = $event"
25
26
  />
26
27
  `,
@@ -34,19 +35,18 @@ The component loads and refreshes status, searches the payer directory, saves bi
34
35
 
35
36
  Procedure entry always keeps one empty row after the entered lines. Starting a code, modifier, or non-default unit count opens the next row automatically; the empty row is never included in save or submit payloads.
36
37
 
37
- Add one authenticated server endpoint. It verifies that the signed-in user may access the bill, then mints a short-lived token bound to that bill and browser origin.
38
+ Add one authenticated server endpoint. It maps the signed-in user's role to permissions and mints a short-lived token bound to your organization, that user, and the browser origin.
38
39
 
39
40
  ```ts
40
41
  import { MindBillClient } from "@mindbill/node";
41
42
 
42
43
  const mindbill = new MindBillClient({ apiKey: process.env["MINDBILL_API_KEY"]! });
43
44
 
44
- app.post("/api/mindbill/bill-session", requireUser, async (req, res) => {
45
- await requireBillAccess(req.user, req.body.billId);
45
+ app.post("/api/mindbill/session", requireUser, async (req, res) => {
46
46
  const session = await mindbill.createBrowserSession({
47
- component: "bill-review",
48
- billId: req.body.billId,
47
+ subject: req.user.id,
49
48
  allowedOrigin: `${req.protocol}://${req.get("host")}`,
49
+ permissions: billingPermissionsFor(req.user.role),
50
50
  expiresIn: 900,
51
51
  });
52
52
  res.json(session);
@@ -14,10 +14,14 @@ class MindBillLifecycleStore {
14
14
  mutating = signal(false, ...(ngDevMode ? [{ debugName: "mutating" }] : /* istanbul ignore next */ []));
15
15
  ready = computed(() => this.data() !== null && !this.loading(), ...(ngDevMode ? [{ debugName: "ready" }] : /* istanbul ignore next */ []));
16
16
  client = null;
17
+ createInput;
18
+ onBillCreated;
17
19
  refreshTimer = null;
18
- connect(options, refreshInterval = 60_000) {
20
+ connect(options, refreshInterval = 60_000, createInput, onBillCreated) {
19
21
  this.disconnect();
20
- this.billId.set(options.billId);
22
+ this.billId.set(options.billId ?? "");
23
+ this.createInput = createInput;
24
+ this.onBillCreated = onBillCreated;
21
25
  this.client = createBillLifecycleClient(options);
22
26
  void this.refresh();
23
27
  if (refreshInterval > 0) {
@@ -30,13 +34,29 @@ class MindBillLifecycleStore {
30
34
  this.refreshTimer = null;
31
35
  this.client?.clearSession();
32
36
  this.client = null;
37
+ this.createInput = undefined;
38
+ this.onBillCreated = undefined;
33
39
  }
34
40
  async refresh() {
35
41
  if (!this.client)
36
42
  return null;
37
43
  this.loading.set(this.data() === null);
38
44
  try {
39
- const data = await this.client.getLifecycle();
45
+ let data;
46
+ if (!this.client.getBillId()) {
47
+ if (!this.createInput) {
48
+ throw new Error("Pass billId to open a bill or create to start a new one.");
49
+ }
50
+ const created = await this.client.createBill(this.createInput);
51
+ const isNew = !this.billId();
52
+ this.billId.set(created.billId);
53
+ if (isNew)
54
+ this.onBillCreated?.(created.billId, created.data);
55
+ data = created.data;
56
+ }
57
+ else {
58
+ data = await this.client.getLifecycle();
59
+ }
40
60
  this.data.set(data);
41
61
  this.error.set(null);
42
62
  return data;
@@ -148,17 +168,27 @@ const DOCUMENT_TYPES = [
148
168
  ];
149
169
  class MindBillBillLifecycleComponent {
150
170
  billId = "";
151
- sessionEndpoint = "/api/mindbill/bill-session";
171
+ create;
172
+ sessionEndpoint = "/api/mindbill/session";
152
173
  apiBaseUrl = "https://app.mindbill.org";
153
174
  getSession;
154
175
  refreshInterval = 60_000;
155
176
  appearance = { preset: "mindbill" };
156
177
  billIdChange = new EventEmitter();
178
+ billCreated = new EventEmitter();
157
179
  submitted = new EventEmitter();
158
180
  billingError = new EventEmitter();
159
181
  store = new MindBillLifecycleStore();
160
182
  documentTypes = DOCUMENT_TYPES;
161
183
  medLegalCodes = ["ML200", "ML201", "ML202", "ML203", "ML204", "ML205", "ML206", "MLPRR"];
184
+ lifecycleStages = [
185
+ { id: "created", label: "Created" },
186
+ { id: "submitted", label: "Submitted" },
187
+ { id: "accepted", label: "Accepted" },
188
+ { id: "response", label: "Payer response" },
189
+ { id: "follow_up", label: "Follow-up" },
190
+ { id: "closed", label: "Closed" },
191
+ ];
162
192
  draft = null;
163
193
  dirty = false;
164
194
  payerQuery = "";
@@ -172,8 +202,9 @@ class MindBillBillLifecycleComponent {
172
202
  panel = "";
173
203
  notice = "";
174
204
  closeReason = "";
175
- payment = { amount: 0, method: "check", depositDate: new Date().toISOString().slice(0, 10) };
205
+ payment = { amount: 0, method: "check", checkNumber: "", depositDate: new Date().toISOString().slice(0, 10), note: "" };
176
206
  review = { reason: "", payerClaimControlNumber: "", disputedAmount: 0 };
207
+ reviewAttachmentIds = [];
177
208
  payerTimer = null;
178
209
  constructor() {
179
210
  effect(() => {
@@ -186,8 +217,20 @@ class MindBillBillLifecycleComponent {
186
217
  });
187
218
  }
188
219
  ngOnChanges(changes) {
189
- if ((changes["billId"] || changes["sessionEndpoint"] || changes["apiBaseUrl"] || changes["getSession"]) && this.billId) {
190
- this.store.connect({ billId: this.billId, sessionEndpoint: this.sessionEndpoint, apiBaseUrl: this.apiBaseUrl, ...(this.getSession ? { getSession: this.getSession } : {}) }, this.refreshInterval);
220
+ if (changes["billId"] || changes["create"] || changes["sessionEndpoint"] || changes["apiBaseUrl"] || changes["getSession"]) {
221
+ if (!this.billId && !this.create) {
222
+ this.store.disconnect();
223
+ return;
224
+ }
225
+ this.store.connect({
226
+ ...(this.billId ? { billId: this.billId } : {}),
227
+ sessionEndpoint: this.sessionEndpoint,
228
+ apiBaseUrl: this.apiBaseUrl,
229
+ ...(this.getSession ? { getSession: this.getSession } : {}),
230
+ }, this.refreshInterval, this.create, (billId, data) => {
231
+ this.billIdChange.emit(billId);
232
+ this.billCreated.emit({ billId, data });
233
+ });
191
234
  }
192
235
  }
193
236
  ngOnDestroy() { this.store.disconnect(); if (this.payerTimer)
@@ -209,6 +252,24 @@ class MindBillBillLifecycleComponent {
209
252
  }
210
253
  stateLabel(data) { return data.lifecycle.state.replace(/_/g, " ").replace(/\b\w/g, (value) => value.toUpperCase()); }
211
254
  lifecycleDetail(data) { return data.lifecycle.submittedAt ? `Submitted ${new Date(data.lifecycle.submittedAt).toLocaleDateString()}${data.lifecycle.agingDays != null ? ` · ${data.lifecycle.agingDays} days old` : ""}` : "Review the prefilled bill and payer packet before submission."; }
255
+ currentStageIndex(data) {
256
+ const value = data.lifecycle.state.toLowerCase();
257
+ if (value.includes("closed") || value.includes("written_off"))
258
+ return 5;
259
+ if (value.includes("second_review") || value.includes("ibr") || value.includes("lien") || value.includes("appeal"))
260
+ return 4;
261
+ if (value.includes("processed") || value.includes("paid") || value.includes("denied") || value.includes("rejected") || value.includes("response"))
262
+ return 3;
263
+ if (value.includes("accepted"))
264
+ return 2;
265
+ if (value.includes("submitted") || value.includes("queued") || value.includes("sent"))
266
+ return 1;
267
+ return 0;
268
+ }
269
+ activityLabel(type) {
270
+ const labels = { "bill.created": "Bill created", "bill.submitted": "Bill submitted", "bill.accepted": "Bill accepted", "bill.rejected": "Bill rejected", "bill.denied": "Bill denied", "bill.partially_paid": "Partial payment received", "bill.paid": "Bill paid", "bill.second_review": "Second Bill Review submitted", "bill.ibr": "Independent Bill Review submitted", "bill.lien": "Bill moved to lien", "bill.closed": "Bill closed", "payment.posted": "Payment posted", "eor.received": "EOR received" };
271
+ return labels[type] ?? type.replace(/[._-]+/g, " ").replace(/\b\w/g, (value) => value.toUpperCase());
272
+ }
212
273
  isEditable(data) { return data.lifecycle.actions.some((action) => action.id === "edit_and_submit" && action.enabled) || ["incomplete", "draft", "not_submitted"].includes(data.lifecycle.state.toLowerCase()); }
213
274
  payerExplanation(payer) { return payer.signals?.map((signal) => signal.label).join(" · ") || (payer.hasElectronic ? "Electronic billing available" : "Available in payer directory"); }
214
275
  queuePayerSearch() { if (this.payerTimer)
@@ -286,22 +347,51 @@ class MindBillBillLifecycleComponent {
286
347
  }
287
348
  async submit() { const input = this.buildInput(); if (!input || !this.canSubmit() || !this.deliveryReady())
288
349
  return; const data = await this.store.submitBill(input, this.submission); this.showSubmit = false; this.dirty = false; this.draft = this.makeDraft(data); this.notice = "Bill submitted."; this.submitted.emit(data); }
289
- beginAction(action) { if (action === "post_payment")
290
- this.panel = "payment";
291
- else if (action === "second_review" || action === "independent_bill_review")
292
- this.panel = "review";
293
- else if (action === "close")
294
- this.panel = "close";
295
- else if (action === "view_eor" && this.store.data()?.eors[0]) {
296
- const eor = this.store.data().eors[0];
297
- void this.openEor(eor.id, eor.filename);
350
+ beginAction(action) {
351
+ const data = this.store.data();
352
+ if (action === "post_payment")
353
+ this.panel = "payment";
354
+ else if (action === "second_review" || action === "independent_bill_review") {
355
+ this.reviewAttachmentIds = data?.bill.attachments.map((doc) => doc.id) ?? [];
356
+ this.panel = "review";
357
+ }
358
+ else if (action === "close")
359
+ this.panel = "close";
360
+ else if (action === "view_eor" && data?.eors[0]) {
361
+ const eor = data.eors[0];
362
+ void this.openEor(eor.id, eor.filename);
363
+ }
364
+ else if (action === "correct_and_resubmit")
365
+ this.panel = "correction";
366
+ }
367
+ actionTitle() { return this.panel === "payment" ? "Post payment" : this.panel === "review" ? "Submit Second Review" : this.panel === "correction" ? "Correct and resubmit" : "Close bill"; }
368
+ toggleReviewAttachment(id) { this.reviewAttachmentIds = this.reviewAttachmentIds.includes(id) ? this.reviewAttachmentIds.filter((item) => item !== id) : [...this.reviewAttachmentIds, id]; }
369
+ async attachForReview() { if (!this.pendingFile)
370
+ return; const filename = this.pendingFile.name; await this.attach(); const attachment = this.store.data()?.bill.attachments.find((doc) => doc.filename === filename); if (attachment && !this.reviewAttachmentIds.includes(attachment.id))
371
+ this.reviewAttachmentIds = [...this.reviewAttachmentIds, attachment.id]; }
372
+ async correct() { const data = await this.store.startCorrection(); this.billIdChange.emit(this.store.billId()); this.dirty = false; this.draft = this.makeDraft(data); this.panel = ""; this.notice = "Correction ready for review."; }
373
+ async postPayment() {
374
+ await this.store.postPayment({
375
+ amount: this.payment.amount,
376
+ method: this.payment.method,
377
+ depositDate: this.payment.depositDate,
378
+ ...(this.payment.checkNumber ? { checkNumber: this.payment.checkNumber } : {}),
379
+ ...(this.payment.note ? { note: this.payment.note } : {}),
380
+ });
381
+ this.panel = "";
382
+ this.notice = "Payment posted.";
383
+ }
384
+ async submitReview() {
385
+ await this.store.submitSecondReview({
386
+ reason: this.review.reason,
387
+ payerClaimControlNumber: this.review.payerClaimControlNumber,
388
+ disputedAmount: this.review.disputedAmount > 0 ? this.review.disputedAmount : undefined,
389
+ route: "ebill",
390
+ attachmentIds: this.reviewAttachmentIds,
391
+ });
392
+ this.panel = "";
393
+ this.notice = "Second Review submitted.";
298
394
  }
299
- else if (action === "correct_and_resubmit")
300
- void this.correct(); }
301
- async correct() { const data = await this.store.startCorrection(); this.billIdChange.emit(this.store.billId()); this.dirty = false; this.draft = this.makeDraft(data); this.notice = "Correction draft created."; }
302
- async postPayment() { await this.store.postPayment({ ...this.payment }); this.panel = ""; this.notice = "Payment posted."; }
303
- async submitReview() { const data = this.store.data(); if (!data)
304
- return; await this.store.submitSecondReview({ ...this.review, disputedAmount: this.review.disputedAmount || undefined, route: "ebill", attachmentIds: data.bill.attachments.map((doc) => doc.id) }); this.panel = ""; this.notice = "Second Review submitted."; }
305
395
  async closeBill() { if (!this.closeReason.trim())
306
396
  return; await this.store.closeBill({ reason: this.closeReason }); this.panel = ""; this.notice = "Bill closed."; }
307
397
  buildInput() {
@@ -348,7 +438,7 @@ class MindBillBillLifecycleComponent {
348
438
  this.draft.bill.lineItems = ensureTrailingProcedureLine(this.draft.bill.lineItems); }
349
439
  openBlob(blob, filename) { const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.target = "_blank"; link.rel = "noopener"; link.download = filename; link.click(); setTimeout(() => URL.revokeObjectURL(url), 60_000); }
350
440
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: MindBillBillLifecycleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
351
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: MindBillBillLifecycleComponent, isStandalone: true, selector: "mindbill-bill-lifecycle", inputs: { billId: "billId", sessionEndpoint: "sessionEndpoint", apiBaseUrl: "apiBaseUrl", getSession: "getSession", refreshInterval: "refreshInterval", appearance: "appearance" }, outputs: { billIdChange: "billIdChange", submitted: "submitted", billingError: "billingError" }, usesOnChanges: true, ngImport: i0, template: `
441
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: MindBillBillLifecycleComponent, isStandalone: true, selector: "mindbill-bill-lifecycle", inputs: { billId: "billId", create: "create", sessionEndpoint: "sessionEndpoint", apiBaseUrl: "apiBaseUrl", getSession: "getSession", refreshInterval: "refreshInterval", appearance: "appearance" }, outputs: { billIdChange: "billIdChange", billCreated: "billCreated", submitted: "submitted", billingError: "billingError" }, usesOnChanges: true, ngImport: i0, template: `
352
442
  <section class="mb" [ngStyle]="themeStyle">
353
443
  @if (store.loading() && !store.data()) {
354
444
  <div class="state">Loading billing…</div>
@@ -360,6 +450,16 @@ class MindBillBillLifecycleComponent {
360
450
  <div class="money"><span>{{ data.bill.balanceDue | currency }}</span><small>balance</small></div>
361
451
  </header>
362
452
 
453
+ <section class="card progress" aria-label="Bill lifecycle">
454
+ <div class="card-title"><div><h3>Bill status</h3><p>{{ lifecycleDetail(data) }}</p></div><span>{{ stateLabel(data) }}</span></div>
455
+ <ol>@for (stage of lifecycleStages; track stage.id; let i=$index) { <li [class.complete]="i < currentStageIndex(data)" [class.current]="i === currentStageIndex(data)" [attr.aria-current]="i === currentStageIndex(data) ? 'step' : null"><b>{{ i < currentStageIndex(data) ? '✓' : i + 1 }}</b><span>{{ stage.label }}</span></li> }</ol>
456
+ </section>
457
+
458
+ <section class="card snapshot" aria-label="Bill snapshot">
459
+ <div class="card-title"><div><h3>Bill snapshot</h3><p>The claim and financial values currently recorded on this bill.</p></div></div>
460
+ <dl><div><dt>Patient</dt><dd>{{ data.patient.name }}</dd></div><div><dt>Claim</dt><dd>{{ data.injury.claimNumber || '—' }}</dd></div><div><dt>Payer</dt><dd>{{ data.delivery.payerName || data.injury.claimsAdminName || '—' }}</dd></div><div><dt>Date of service</dt><dd>{{ data.bill.dos || '—' }}</dd></div><div><dt>Charged</dt><dd>{{ data.bill.totalCharge | currency }}</dd></div><div><dt>Balance</dt><dd>{{ data.bill.balanceDue | currency }}</dd></div></dl>
461
+ </section>
462
+
363
463
  @if (isEditable(data) && draft) {
364
464
  <form class="review" (submit)="$event.preventDefault(); submit()">
365
465
  <section class="card">
@@ -465,12 +565,27 @@ class MindBillBillLifecycleComponent {
465
565
  } @else {
466
566
  <section class="card lifecycle">
467
567
  <div class="card-title"><div><h3>Billing activity</h3><p>Available actions change automatically with bill status.</p></div><button (click)="store.refresh()">Refresh</button></div>
468
- @if (data.eors.length) { <div class="eors"><h4>Explanation of Review</h4>@for (eor of data.eors; track eor.id) { <button (click)="openEor(eor.id,eor.filename)"><span><strong>{{ eor.filename }}</strong><small>{{ eor.description || 'Payer response' }}</small></span><b>View PDF</b></button> }</div> }
568
+ <div class="lifecycle-grid">
569
+ <section><h4>Remittance</h4><dl class="facts"><div><dt>Payer reported</dt><dd>{{ data.remittance.payerReportedPaid == null ? '—' : (data.remittance.payerReportedPaid | currency) }}</dd></div><div><dt>Posted</dt><dd>{{ data.remittance.totalPaid | currency }}</dd></div><div><dt>Balance</dt><dd>{{ data.remittance.balanceDue | currency }}</dd></div></dl>@if (data.remittance.denialReason) { <div class="denial"><strong>Denial reason</strong><p>{{ data.remittance.denialReason }}</p></div> }</section>
570
+ <section><h4>{{ data.delivery.payerName || 'Claims administrator' }}</h4><dl class="contacts">@if (data.delivery.contacts.adjusterName) { <div><dt>Adjuster</dt><dd>{{ data.delivery.contacts.adjusterName }}</dd></div> }@if (data.delivery.contacts.adjusterPhone) { <div><dt>Phone</dt><dd><a [href]="'tel:'+data.delivery.contacts.adjusterPhone">{{ data.delivery.contacts.adjusterPhone }}</a></dd></div> }@if (data.delivery.contacts.adjusterEmail) { <div><dt>Adjuster email</dt><dd><a [href]="'mailto:'+data.delivery.contacts.adjusterEmail">{{ data.delivery.contacts.adjusterEmail }}</a></dd></div> }@if (data.delivery.contacts.faxNumber) { <div><dt>Fax</dt><dd><a [href]="'tel:'+data.delivery.contacts.faxNumber">{{ data.delivery.contacts.faxNumber }}</a></dd></div> }@if (data.delivery.contacts.claimsEmail) { <div><dt>Claims email</dt><dd><a [href]="'mailto:'+data.delivery.contacts.claimsEmail">{{ data.delivery.contacts.claimsEmail }}</a></dd></div> }@if (data.delivery.contacts.mailingAddress) { <div><dt>Mail</dt><dd>{{ data.delivery.contacts.mailingAddress }}</dd></div> }@if (data.delivery.contacts.portalUrl) { <div><dt>Portal</dt><dd><a [href]="data.delivery.contacts.portalUrl" target="_blank" rel="noopener">Open payer portal</a></dd></div> }</dl></section>
571
+ </div>
572
+ @if (data.eors.length) { <div class="eors"><h4>Explanation of Review</h4><p>Payer response details and the original returned document.</p>@for (eor of data.eors; track eor.id) { <button (click)="openEor(eor.id,eor.filename)"><span><strong>{{ eor.filename }}</strong><small>{{ eor.description || 'Payer response' }}</small></span><b>View original PDF</b></button> }</div> }
573
+ <section class="payments"><h4>Payments</h4>@if (!data.payments.length) { <p>No payments posted.</p> } @else { @for (paymentItem of data.payments; track paymentItem.id) { <article><div><strong>{{ paymentItem.amount | currency }}</strong><span>{{ paymentItem.method | uppercase }} · {{ paymentItem.status }}</span></div><div><span>{{ paymentItem.checkNumber ? '#'+paymentItem.checkNumber : paymentItem.source }}</span><span>{{ paymentItem.depositDate || (paymentItem.postedAt | date:'mediumDate') }}</span></div>@if (paymentItem.note) { <p>{{ paymentItem.note }}</p> }</article> } }</section>
574
+ <section class="history"><h4>Bill history</h4>@if (!data.activity.length) { <p>No bill activity yet.</p> } @else { <ol>@for (event of data.activity; track event.id) { <li><span></span><div><strong>{{ activityLabel(event.type) }}</strong><small>{{ event.createdAt | date:'medium' }}{{ event.actor ? ' · '+event.actor : '' }}</small>@if (event.description) { <p>{{ event.description }}</p> }</div></li> }</ol> }</section>
469
575
  <div class="actionbar">@for (action of data.lifecycle.actions; track action.id) { @if (action.enabled) { <button [class.primary]="action.primary" (click)="beginAction(action.id)">{{ action.label }}</button> } }</div>
470
- @if (panel === 'payment') { <div class="panel"><h4>Post payment</h4><div class="grid three"><label><span>Amount</span><input type="number" min="0.01" [(ngModel)]="payment.amount"></label><label><span>Method</span><select [(ngModel)]="payment.method"><option value="check">Check</option><option value="eft">EFT</option></select></label><label><span>Deposit date</span><input type="date" [(ngModel)]="payment.depositDate"></label></div><div class="actions"><button (click)="panel=''">Cancel</button><button class="primary" (click)="postPayment()">Post payment</button></div></div> }
471
- @if (panel === 'review') { <div class="panel"><h4>Submit Second Review</h4><label><span>Reason</span><textarea [(ngModel)]="review.reason"></textarea></label><div class="grid two"><label><span>Payer control number</span><input [(ngModel)]="review.payerClaimControlNumber"></label><label><span>Disputed amount</span><input type="number" [(ngModel)]="review.disputedAmount"></label></div><div class="actions"><button (click)="panel=''">Cancel</button><button class="primary" (click)="submitReview()">Submit review</button></div></div> }
472
- @if (panel === 'close') { <div class="panel"><h4>Close bill</h4><label><span>Reason</span><textarea [(ngModel)]="closeReason"></textarea></label><div class="actions"><button (click)="panel=''">Cancel</button><button class="danger" (click)="closeBill()">Close bill</button></div></div> }
473
576
  </section>
577
+ @if (panel) {
578
+ <div class="modal-backdrop" (click)="panel=''">
579
+ <section class="submit-modal action-modal" role="dialog" aria-modal="true" (click)="$event.stopPropagation()">
580
+ <header><div><span class="eyebrow">Bill {{ data.bill.billNumber }}</span><h3>{{ actionTitle() }}</h3></div><button class="close" type="button" (click)="panel=''" aria-label="Close">×</button></header>
581
+ @if (panel === 'payment') { <p>Record funds received for this bill. This updates the payment ledger and remaining balance.</p><div class="grid two"><label><span>Amount</span><input type="number" min="0.01" [(ngModel)]="payment.amount"></label><label><span>Method</span><select [(ngModel)]="payment.method"><option value="check">Check</option><option value="eft">EFT</option></select></label><label><span>Check / EFT number</span><input [(ngModel)]="payment.checkNumber"></label><label><span>Deposit date</span><input type="date" [(ngModel)]="payment.depositDate"></label></div><label><span>Note (optional)</span><input [(ngModel)]="payment.note"></label> }
582
+ @if (panel === 'review') { <p>Explain the dispute and choose the exact payer documents to include with the Second Review.</p><label><span>Reason for Second Review</span><textarea [(ngModel)]="review.reason"></textarea></label><div class="grid two"><label><span>Payer control number</span><input [(ngModel)]="review.payerClaimControlNumber"></label><label><span>Disputed amount</span><input type="number" [(ngModel)]="review.disputedAmount"></label></div><fieldset class="review-docs"><legend>Supporting documents</legend>@for (doc of data.bill.attachments; track doc.id) { <label><input type="checkbox" [checked]="reviewAttachmentIds.includes(doc.id)" (change)="toggleReviewAttachment(doc.id)"><span><strong>{{ doc.filename }}</strong><small>{{ doc.description || doc.documentType }}</small></span></label> }</fieldset><div class="upload"><select [(ngModel)]="documentType">@for (type of documentTypes; track type.value) { <option [value]="type.value">{{ type.label }}</option> }</select><input type="file" accept="application/pdf" (change)="fileSelected($event)"><button type="button" [disabled]="!pendingFile || store.mutating()" (click)="attachForReview()">Add document</button></div> }
583
+ @if (panel === 'correction') { <p>Create a replacement bill prefilled from this rejected submission. You can correct the payer, claim, provider, service lines, and packet before resubmitting.</p> }
584
+ @if (panel === 'close') { <p>Closing stops active follow-up. The bill history and documents remain available.</p><label><span>Reason</span><textarea [(ngModel)]="closeReason"></textarea></label> }
585
+ <footer><button type="button" (click)="panel=''">Cancel</button>@if (panel === 'payment') { <button class="primary" type="button" (click)="postPayment()" [disabled]="store.mutating() || payment.amount <= 0">Post payment</button> }@if (panel === 'review') { <button class="primary" type="button" (click)="submitReview()" [disabled]="store.mutating() || !review.reason.trim() || !review.payerClaimControlNumber.trim()">Submit Second Review</button> }@if (panel === 'correction') { <button class="primary" type="button" (click)="correct()" [disabled]="store.mutating()">Create correction</button> }@if (panel === 'close') { <button class="danger" type="button" (click)="closeBill()" [disabled]="store.mutating() || !closeReason.trim()">Close bill</button> }</footer>
586
+ </section>
587
+ </div>
588
+ }
474
589
  }
475
590
  @if (notice) { <div class="notice">{{ notice }}</div> }
476
591
  @if (store.error()) { <div class="notice error">{{ store.error()?.message }}</div> }
@@ -478,7 +593,7 @@ class MindBillBillLifecycleComponent {
478
593
  }
479
594
  <datalist id="mb-states"><option value="CA"></option><option value="AZ"></option><option value="NV"></option><option value="OR"></option><option value="WA"></option><option value="TX"></option><option value="NY"></option></datalist>
480
595
  </section>
481
- `, isInline: true, styles: [":host{display:block}.mb{--a:#238dbd;--ac:#fff;--bg:#f3f8fa;--s:#fff;--t:#203743;--m:#657982;--b:#dbe6ea;--r:12px;--cr:8px;display:grid;gap:16px;color:var(--t);font:14px/1.45 var(--font,Inter,system-ui,sans-serif)}*{box-sizing:border-box}h2,h3,h4,p{margin:0}.summary,.card,.delivery,.state{border:1px solid var(--b);border-radius:var(--r);background:var(--s)}.summary{display:flex;justify-content:space-between;gap:20px;padding:20px}.summary h2{font-size:24px}.summary p,.card p,.delivery p,small{color:var(--m)}.eyebrow{color:var(--m);font-size:11px;font-weight:800;letter-spacing:.13em;text-transform:uppercase}.money{text-align:right}.money span{display:block;font-size:28px;font-weight:800}.money small{font-size:12px}.review{display:grid;gap:16px}.card{padding:20px}.card-title{display:flex;align-items:start;justify-content:space-between;gap:16px;margin-bottom:18px}.card-title h3,.delivery h3{font-size:18px}.card-title>span{border-radius:999px;background:var(--bg);padding:5px 9px;color:var(--m);font-size:11px;font-weight:800;text-transform:uppercase}.grid{display:grid;gap:14px}.grid.four{grid-template-columns:repeat(4,minmax(0,1fr))}.grid.three{grid-template-columns:repeat(3,minmax(0,1fr))}.grid.two{grid-template-columns:repeat(2,minmax(0,1fr))}label{display:grid;gap:6px;color:var(--t);font-size:12px;font-weight:700}input,select,textarea,button{font:inherit}input,select,textarea{width:100%;min-height:42px;border:1px solid var(--b);border-radius:var(--cr);background:#fff;color:var(--t);padding:9px 11px}textarea{min-height:90px;resize:vertical}button{min-height:38px;border:1px solid var(--b);border-radius:var(--cr);background:#fff;color:var(--t);padding:8px 13px;cursor:pointer;font-weight:700}button.primary{border-color:var(--a);background:var(--a);color:var(--ac)}button.danger{border-color:#d4380d;background:#d4380d;color:#fff}button:disabled{cursor:not-allowed;opacity:.5}.subhead{margin:18px 0 9px;border-top:1px solid var(--b);padding-top:15px;font-size:12px;font-weight:800}.selected{display:flex;align-items:center;justify-content:space-between;border:1px solid var(--b);border-radius:var(--cr);padding:12px}.selected div{display:grid}.results{position:relative;z-index:3;display:grid;margin-top:5px;border:1px solid var(--b);border-radius:var(--cr);background:#fff;box-shadow:0 12px 30px #1f2d3d1f;overflow:hidden}.results button{display:grid;gap:2px;text-align:left;border:0;border-bottom:1px solid var(--b);border-radius:0;padding:12px}.results span{color:var(--m);font-weight:400}.presets{display:flex;gap:8px;margin:-4px 0 12px}.lines{display:grid;gap:10px}.line{display:grid;grid-template-columns:1.2fr 1.2fr 110px 120px 40px;align-items:end;gap:10px;border-radius:var(--cr);background:var(--bg);padding:12px}.line.professional{grid-template-columns:1.1fr 1fr 1fr 1fr 110px 1fr 90px 110px 40px}.allowed{display:grid;gap:7px;text-align:right}.allowed span{color:var(--m);font-size:11px;font-weight:800;text-transform:uppercase}.allowed strong{font-size:17px}.remove{border:0;background:transparent;font-size:20px}.docs{list-style:none;margin:0;padding:0}.docs li{display:flex;align-items:center;gap:8px;border-top:1px solid var(--b);padding:11px 0}.docs li>div{display:grid;flex:1}.upload{display:grid;grid-template-columns:240px 1fr auto;gap:10px;margin-top:12px;border-radius:var(--cr);background:var(--bg);padding:12px}.delivery{display:flex;align-items:center;justify-content:space-between;gap:24px;padding:20px;background:color-mix(in srgb,var(--a) 5%,white)}.actions,.actionbar{display:flex;justify-content:flex-end;gap:8px}.lifecycle{display:grid;gap:16px}.actionbar{flex-wrap:wrap}.eors{display:grid;gap:8px}.eors>button{display:flex;align-items:center;justify-content:space-between;text-align:left}.eors span{display:grid}.panel{display:grid;gap:14px;border-top:1px solid var(--b);padding-top:16px}.notice,.state{padding:13px 15px}.notice{border-radius:var(--cr);background:#edf8f2;color:#23734c}.notice.error,.state.error{background:#fff2f0;color:#b42318}.state{display:grid;gap:10px}.state button{justify-self:start}.powered{text-align:right;color:var(--m);font-size:11px}.modal-backdrop{position:fixed;z-index:1000;inset:0;display:grid;place-items:center;background:#0f20288f;padding:20px}.submit-modal{display:grid;gap:18px;width:min(680px,100%);max-height:calc(100vh - 40px);overflow:auto;border:1px solid var(--b);border-radius:var(--r);background:var(--s);padding:22px;box-shadow:0 24px 80px #0f202847}.submit-modal>header,.submit-modal>footer{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}.submit-modal>footer{justify-content:flex-end}.submit-modal .close{border:0;background:transparent;font-size:26px}.route-list{display:grid;gap:8px}.route-option{display:grid;grid-template-columns:20px 1fr auto;align-items:center;gap:12px;text-align:left;padding:13px}.route-option>span:nth-child(2){display:grid}.route-option.selected{border-color:var(--a);box-shadow:0 0 0 2px color-mix(in srgb,var(--a) 18%,transparent)}.route-option .radio{width:16px;height:16px;border:2px solid var(--b);border-radius:50%}.route-option.selected .radio{border:5px solid var(--a)}.route-option b{color:var(--a);font-size:11px;text-transform:uppercase}@media(max-width:1100px){.line.professional{grid-template-columns:repeat(3,minmax(0,1fr))}.line.professional .allowed{grid-column:auto}}@media(max-width:900px){.grid.four,.grid.three{grid-template-columns:repeat(2,minmax(0,1fr))}.line{grid-template-columns:1fr 1fr 90px}.allowed{grid-column:1/-2;text-align:left}.upload{grid-template-columns:1fr}.delivery{align-items:stretch;flex-direction:column}.actions{justify-content:flex-start}}@media(max-width:600px){.summary,.card-title{align-items:stretch;flex-direction:column}.money{text-align:left}.grid.four,.grid.three,.grid.two,.line,.line.professional{grid-template-columns:1fr}.allowed{grid-column:auto}.docs li{align-items:flex-start;flex-wrap:wrap}.modal-backdrop{padding:8px}.submit-modal{max-height:calc(100vh - 16px);padding:16px}.route-option{grid-template-columns:20px 1fr}.route-option b{grid-column:2}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { 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.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "pipe", type: i1.CurrencyPipe, name: "currency" }] });
596
+ `, isInline: true, styles: [":host{display:block}.mb{--a:#238dbd;--ac:#fff;--bg:#f3f8fa;--s:#fff;--t:#203743;--m:#657982;--b:#dbe6ea;--r:12px;--cr:8px;display:grid;gap:16px;color:var(--t);font:14px/1.45 var(--font,Inter,system-ui,sans-serif)}*{box-sizing:border-box}h2,h3,h4,p{margin:0}a{color:var(--a)}.summary,.card,.delivery,.state{border:1px solid var(--b);border-radius:var(--r);background:var(--s)}.summary{display:flex;justify-content:space-between;gap:20px;padding:20px}.summary h2{font-size:24px}.summary p,.card p,.delivery p,small{color:var(--m)}.eyebrow{color:var(--m);font-size:11px;font-weight:800;letter-spacing:.13em;text-transform:uppercase}.money{text-align:right}.money span{display:block;font-size:28px;font-weight:800}.money small{font-size:12px}.review{display:grid;gap:16px}.card{padding:20px}.card-title{display:flex;align-items:start;justify-content:space-between;gap:16px;margin-bottom:18px}.card-title h3,.delivery h3{font-size:18px}.card-title>span{border-radius:999px;background:var(--bg);padding:5px 9px;color:var(--m);font-size:11px;font-weight:800;text-transform:uppercase}.grid{display:grid;gap:14px}.grid.four{grid-template-columns:repeat(4,minmax(0,1fr))}.grid.three{grid-template-columns:repeat(3,minmax(0,1fr))}.grid.two{grid-template-columns:repeat(2,minmax(0,1fr))}label{display:grid;gap:6px;color:var(--t);font-size:12px;font-weight:700}input,select,textarea,button{font:inherit}input,select,textarea{width:100%;min-height:42px;border:1px solid var(--b);border-radius:var(--cr);background:#fff;color:var(--t);padding:9px 11px}textarea{min-height:90px;resize:vertical}button{min-height:38px;border:1px solid var(--b);border-radius:var(--cr);background:#fff;color:var(--t);padding:8px 13px;cursor:pointer;font-weight:700}button.primary{border-color:var(--a);background:var(--a);color:var(--ac)}button.danger{border-color:#d4380d;background:#d4380d;color:#fff}button:disabled{cursor:not-allowed;opacity:.5}.subhead{margin:18px 0 9px;border-top:1px solid var(--b);padding-top:15px;font-size:12px;font-weight:800}.selected{display:flex;align-items:center;justify-content:space-between;border:1px solid var(--b);border-radius:var(--cr);padding:12px}.selected div{display:grid}.results{position:relative;z-index:3;display:grid;margin-top:5px;border:1px solid var(--b);border-radius:var(--cr);background:#fff;box-shadow:0 12px 30px #1f2d3d1f;overflow:hidden}.results button{display:grid;gap:2px;text-align:left;border:0;border-bottom:1px solid var(--b);border-radius:0;padding:12px}.results span{color:var(--m);font-weight:400}.presets{display:flex;gap:8px;margin:-4px 0 12px}.lines{display:grid;gap:10px}.line{display:grid;grid-template-columns:1.2fr 1.2fr 110px 120px 40px;align-items:end;gap:10px;border-radius:var(--cr);background:var(--bg);padding:12px}.line.professional{grid-template-columns:1.1fr 1fr 1fr 1fr 110px 1fr 90px 110px 40px}.allowed{display:grid;gap:7px;text-align:right}.allowed span{color:var(--m);font-size:11px;font-weight:800;text-transform:uppercase}.allowed strong{font-size:17px}.remove{border:0;background:transparent;font-size:20px}.docs{list-style:none;margin:0;padding:0}.docs li{display:flex;align-items:center;gap:8px;border-top:1px solid var(--b);padding:11px 0}.docs li>div{display:grid;flex:1}.upload{display:grid;grid-template-columns:240px 1fr auto;gap:10px;margin-top:12px;border-radius:var(--cr);background:var(--bg);padding:12px}.delivery{display:flex;align-items:center;justify-content:space-between;gap:24px;padding:20px;background:color-mix(in srgb,var(--a) 5%,white)}.actions,.actionbar{display:flex;justify-content:flex-end;gap:8px}.progress ol{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));list-style:none;margin:0;padding:0}.progress li{position:relative;display:grid;justify-items:center;gap:7px;color:var(--m);font-size:12px;text-align:center}.progress li:before{content:\"\";position:absolute;top:14px;right:50%;left:-50%;height:2px;background:var(--b)}.progress li:first-child:before{display:none}.progress li b{position:relative;z-index:1;display:grid;place-items:center;width:28px;height:28px;border:2px solid var(--b);border-radius:50%;background:var(--s)}.progress li.complete,.progress li.current{color:var(--t)}.progress li.complete:before,.progress li.current:before{background:var(--a)}.progress li.complete b,.progress li.current b{border-color:var(--a);background:var(--a);color:var(--ac)}.snapshot dl,.facts{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));margin:0}.snapshot dl>div,.facts>div{display:grid;gap:3px;border-right:1px solid var(--b);padding:4px 14px}.snapshot dl>div:nth-child(3n),.facts>div:last-child{border-right:0}.snapshot dt,.facts dt,.contacts dt{color:var(--m);font-size:11px;font-weight:800;text-transform:uppercase}.snapshot dd,.facts dd,.contacts dd{margin:0;font-weight:700}.lifecycle{display:grid;gap:20px}.actionbar{flex-wrap:wrap;border-top:1px solid var(--b);padding-top:18px}.lifecycle-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px}.lifecycle-grid>section,.payments,.history,.eors{display:grid;gap:11px;border:1px solid var(--b);border-radius:var(--cr);padding:15px}.contacts{display:grid;gap:9px;margin:0}.contacts>div{display:grid;grid-template-columns:70px 1fr;gap:10px}.denial{border-radius:var(--cr);background:#fff4ed;padding:11px}.eors>button{display:flex;align-items:center;justify-content:space-between;text-align:left}.eors span{display:grid}.payments article{display:grid;grid-template-columns:1fr auto;gap:4px;border-top:1px solid var(--b);padding-top:10px}.payments article>div{display:grid}.payments article>div:last-of-type{text-align:right}.payments article p{grid-column:1/-1}.history ol{display:grid;gap:0;list-style:none;margin:0;padding:0}.history li{display:grid;grid-template-columns:18px 1fr;gap:9px}.history li>span{position:relative;margin-top:5px;width:10px;height:10px;border-radius:50%;background:var(--a)}.history li>span:after{content:\"\";position:absolute;top:10px;bottom:-30px;left:4px;width:2px;background:var(--b)}.history li:last-child>span:after{display:none}.history li>div{display:grid;gap:2px;padding-bottom:15px}.notice,.state{padding:13px 15px}.notice{border-radius:var(--cr);background:#edf8f2;color:#23734c}.notice.error,.state.error{background:#fff2f0;color:#b42318}.state{display:grid;gap:10px}.state button{justify-self:start}.powered{text-align:right;color:var(--m);font-size:11px}.modal-backdrop{position:fixed;z-index:1000;inset:0;display:grid;place-items:center;background:#0f20288f;padding:20px}.submit-modal{display:grid;gap:18px;width:min(680px,100%);max-height:calc(100vh - 40px);overflow:auto;border:1px solid var(--b);border-radius:var(--r);background:var(--s);padding:22px;box-shadow:0 24px 80px #0f202847}.submit-modal>header,.submit-modal>footer{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}.submit-modal>footer{justify-content:flex-end}.submit-modal .close{border:0;background:transparent;font-size:26px}.route-list{display:grid;gap:8px}.route-option{display:grid;grid-template-columns:20px 1fr auto;align-items:center;gap:12px;text-align:left;padding:13px}.route-option>span:nth-child(2){display:grid}.route-option.selected{border-color:var(--a);box-shadow:0 0 0 2px color-mix(in srgb,var(--a) 18%,transparent)}.route-option .radio{width:16px;height:16px;border:2px solid var(--b);border-radius:50%}.route-option.selected .radio{border:5px solid var(--a)}.route-option b{color:var(--a);font-size:11px;text-transform:uppercase}.action-modal{width:min(720px,100%)}.review-docs{display:grid;gap:8px;border:1px solid var(--b);border-radius:var(--cr);padding:12px}.review-docs label{display:grid;grid-template-columns:20px 1fr;align-items:start}.review-docs input{min-height:auto}.review-docs label>span{display:grid}@media(max-width:1100px){.line.professional{grid-template-columns:repeat(3,minmax(0,1fr))}.line.professional .allowed{grid-column:auto}}@media(max-width:900px){.grid.four,.grid.three{grid-template-columns:repeat(2,minmax(0,1fr))}.line{grid-template-columns:1fr 1fr 90px}.allowed{grid-column:1/-2;text-align:left}.upload{grid-template-columns:1fr}.delivery{align-items:stretch;flex-direction:column}.actions{justify-content:flex-start}.lifecycle-grid{grid-template-columns:1fr}}@media(max-width:600px){.summary,.card-title{align-items:stretch;flex-direction:column}.money{text-align:left}.grid.four,.grid.three,.grid.two,.line,.line.professional{grid-template-columns:1fr}.allowed{grid-column:auto}.docs li{align-items:flex-start;flex-wrap:wrap}.progress{overflow-x:auto}.progress ol{min-width:560px}.snapshot dl,.facts{grid-template-columns:1fr}.snapshot dl>div,.facts>div{border-right:0;border-bottom:1px solid var(--b);padding:10px 0}.snapshot dl>div:last-child,.facts>div:last-child{border-bottom:0}.modal-backdrop{align-items:end;padding:0}.submit-modal{max-height:92vh;border-radius:var(--r) var(--r) 0 0;padding:16px}.route-option{grid-template-columns:20px 1fr}.route-option b{grid-column:2}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { 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.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "pipe", type: i1.UpperCasePipe, name: "uppercase" }, { kind: "pipe", type: i1.CurrencyPipe, name: "currency" }, { kind: "pipe", type: i1.DatePipe, name: "date" }] });
482
597
  }
483
598
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: MindBillBillLifecycleComponent, decorators: [{
484
599
  type: Component,
@@ -494,6 +609,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
494
609
  <div class="money"><span>{{ data.bill.balanceDue | currency }}</span><small>balance</small></div>
495
610
  </header>
496
611
 
612
+ <section class="card progress" aria-label="Bill lifecycle">
613
+ <div class="card-title"><div><h3>Bill status</h3><p>{{ lifecycleDetail(data) }}</p></div><span>{{ stateLabel(data) }}</span></div>
614
+ <ol>@for (stage of lifecycleStages; track stage.id; let i=$index) { <li [class.complete]="i < currentStageIndex(data)" [class.current]="i === currentStageIndex(data)" [attr.aria-current]="i === currentStageIndex(data) ? 'step' : null"><b>{{ i < currentStageIndex(data) ? '✓' : i + 1 }}</b><span>{{ stage.label }}</span></li> }</ol>
615
+ </section>
616
+
617
+ <section class="card snapshot" aria-label="Bill snapshot">
618
+ <div class="card-title"><div><h3>Bill snapshot</h3><p>The claim and financial values currently recorded on this bill.</p></div></div>
619
+ <dl><div><dt>Patient</dt><dd>{{ data.patient.name }}</dd></div><div><dt>Claim</dt><dd>{{ data.injury.claimNumber || '—' }}</dd></div><div><dt>Payer</dt><dd>{{ data.delivery.payerName || data.injury.claimsAdminName || '—' }}</dd></div><div><dt>Date of service</dt><dd>{{ data.bill.dos || '—' }}</dd></div><div><dt>Charged</dt><dd>{{ data.bill.totalCharge | currency }}</dd></div><div><dt>Balance</dt><dd>{{ data.bill.balanceDue | currency }}</dd></div></dl>
620
+ </section>
621
+
497
622
  @if (isEditable(data) && draft) {
498
623
  <form class="review" (submit)="$event.preventDefault(); submit()">
499
624
  <section class="card">
@@ -599,12 +724,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
599
724
  } @else {
600
725
  <section class="card lifecycle">
601
726
  <div class="card-title"><div><h3>Billing activity</h3><p>Available actions change automatically with bill status.</p></div><button (click)="store.refresh()">Refresh</button></div>
602
- @if (data.eors.length) { <div class="eors"><h4>Explanation of Review</h4>@for (eor of data.eors; track eor.id) { <button (click)="openEor(eor.id,eor.filename)"><span><strong>{{ eor.filename }}</strong><small>{{ eor.description || 'Payer response' }}</small></span><b>View PDF</b></button> }</div> }
727
+ <div class="lifecycle-grid">
728
+ <section><h4>Remittance</h4><dl class="facts"><div><dt>Payer reported</dt><dd>{{ data.remittance.payerReportedPaid == null ? '—' : (data.remittance.payerReportedPaid | currency) }}</dd></div><div><dt>Posted</dt><dd>{{ data.remittance.totalPaid | currency }}</dd></div><div><dt>Balance</dt><dd>{{ data.remittance.balanceDue | currency }}</dd></div></dl>@if (data.remittance.denialReason) { <div class="denial"><strong>Denial reason</strong><p>{{ data.remittance.denialReason }}</p></div> }</section>
729
+ <section><h4>{{ data.delivery.payerName || 'Claims administrator' }}</h4><dl class="contacts">@if (data.delivery.contacts.adjusterName) { <div><dt>Adjuster</dt><dd>{{ data.delivery.contacts.adjusterName }}</dd></div> }@if (data.delivery.contacts.adjusterPhone) { <div><dt>Phone</dt><dd><a [href]="'tel:'+data.delivery.contacts.adjusterPhone">{{ data.delivery.contacts.adjusterPhone }}</a></dd></div> }@if (data.delivery.contacts.adjusterEmail) { <div><dt>Adjuster email</dt><dd><a [href]="'mailto:'+data.delivery.contacts.adjusterEmail">{{ data.delivery.contacts.adjusterEmail }}</a></dd></div> }@if (data.delivery.contacts.faxNumber) { <div><dt>Fax</dt><dd><a [href]="'tel:'+data.delivery.contacts.faxNumber">{{ data.delivery.contacts.faxNumber }}</a></dd></div> }@if (data.delivery.contacts.claimsEmail) { <div><dt>Claims email</dt><dd><a [href]="'mailto:'+data.delivery.contacts.claimsEmail">{{ data.delivery.contacts.claimsEmail }}</a></dd></div> }@if (data.delivery.contacts.mailingAddress) { <div><dt>Mail</dt><dd>{{ data.delivery.contacts.mailingAddress }}</dd></div> }@if (data.delivery.contacts.portalUrl) { <div><dt>Portal</dt><dd><a [href]="data.delivery.contacts.portalUrl" target="_blank" rel="noopener">Open payer portal</a></dd></div> }</dl></section>
730
+ </div>
731
+ @if (data.eors.length) { <div class="eors"><h4>Explanation of Review</h4><p>Payer response details and the original returned document.</p>@for (eor of data.eors; track eor.id) { <button (click)="openEor(eor.id,eor.filename)"><span><strong>{{ eor.filename }}</strong><small>{{ eor.description || 'Payer response' }}</small></span><b>View original PDF</b></button> }</div> }
732
+ <section class="payments"><h4>Payments</h4>@if (!data.payments.length) { <p>No payments posted.</p> } @else { @for (paymentItem of data.payments; track paymentItem.id) { <article><div><strong>{{ paymentItem.amount | currency }}</strong><span>{{ paymentItem.method | uppercase }} · {{ paymentItem.status }}</span></div><div><span>{{ paymentItem.checkNumber ? '#'+paymentItem.checkNumber : paymentItem.source }}</span><span>{{ paymentItem.depositDate || (paymentItem.postedAt | date:'mediumDate') }}</span></div>@if (paymentItem.note) { <p>{{ paymentItem.note }}</p> }</article> } }</section>
733
+ <section class="history"><h4>Bill history</h4>@if (!data.activity.length) { <p>No bill activity yet.</p> } @else { <ol>@for (event of data.activity; track event.id) { <li><span></span><div><strong>{{ activityLabel(event.type) }}</strong><small>{{ event.createdAt | date:'medium' }}{{ event.actor ? ' · '+event.actor : '' }}</small>@if (event.description) { <p>{{ event.description }}</p> }</div></li> }</ol> }</section>
603
734
  <div class="actionbar">@for (action of data.lifecycle.actions; track action.id) { @if (action.enabled) { <button [class.primary]="action.primary" (click)="beginAction(action.id)">{{ action.label }}</button> } }</div>
604
- @if (panel === 'payment') { <div class="panel"><h4>Post payment</h4><div class="grid three"><label><span>Amount</span><input type="number" min="0.01" [(ngModel)]="payment.amount"></label><label><span>Method</span><select [(ngModel)]="payment.method"><option value="check">Check</option><option value="eft">EFT</option></select></label><label><span>Deposit date</span><input type="date" [(ngModel)]="payment.depositDate"></label></div><div class="actions"><button (click)="panel=''">Cancel</button><button class="primary" (click)="postPayment()">Post payment</button></div></div> }
605
- @if (panel === 'review') { <div class="panel"><h4>Submit Second Review</h4><label><span>Reason</span><textarea [(ngModel)]="review.reason"></textarea></label><div class="grid two"><label><span>Payer control number</span><input [(ngModel)]="review.payerClaimControlNumber"></label><label><span>Disputed amount</span><input type="number" [(ngModel)]="review.disputedAmount"></label></div><div class="actions"><button (click)="panel=''">Cancel</button><button class="primary" (click)="submitReview()">Submit review</button></div></div> }
606
- @if (panel === 'close') { <div class="panel"><h4>Close bill</h4><label><span>Reason</span><textarea [(ngModel)]="closeReason"></textarea></label><div class="actions"><button (click)="panel=''">Cancel</button><button class="danger" (click)="closeBill()">Close bill</button></div></div> }
607
735
  </section>
736
+ @if (panel) {
737
+ <div class="modal-backdrop" (click)="panel=''">
738
+ <section class="submit-modal action-modal" role="dialog" aria-modal="true" (click)="$event.stopPropagation()">
739
+ <header><div><span class="eyebrow">Bill {{ data.bill.billNumber }}</span><h3>{{ actionTitle() }}</h3></div><button class="close" type="button" (click)="panel=''" aria-label="Close">×</button></header>
740
+ @if (panel === 'payment') { <p>Record funds received for this bill. This updates the payment ledger and remaining balance.</p><div class="grid two"><label><span>Amount</span><input type="number" min="0.01" [(ngModel)]="payment.amount"></label><label><span>Method</span><select [(ngModel)]="payment.method"><option value="check">Check</option><option value="eft">EFT</option></select></label><label><span>Check / EFT number</span><input [(ngModel)]="payment.checkNumber"></label><label><span>Deposit date</span><input type="date" [(ngModel)]="payment.depositDate"></label></div><label><span>Note (optional)</span><input [(ngModel)]="payment.note"></label> }
741
+ @if (panel === 'review') { <p>Explain the dispute and choose the exact payer documents to include with the Second Review.</p><label><span>Reason for Second Review</span><textarea [(ngModel)]="review.reason"></textarea></label><div class="grid two"><label><span>Payer control number</span><input [(ngModel)]="review.payerClaimControlNumber"></label><label><span>Disputed amount</span><input type="number" [(ngModel)]="review.disputedAmount"></label></div><fieldset class="review-docs"><legend>Supporting documents</legend>@for (doc of data.bill.attachments; track doc.id) { <label><input type="checkbox" [checked]="reviewAttachmentIds.includes(doc.id)" (change)="toggleReviewAttachment(doc.id)"><span><strong>{{ doc.filename }}</strong><small>{{ doc.description || doc.documentType }}</small></span></label> }</fieldset><div class="upload"><select [(ngModel)]="documentType">@for (type of documentTypes; track type.value) { <option [value]="type.value">{{ type.label }}</option> }</select><input type="file" accept="application/pdf" (change)="fileSelected($event)"><button type="button" [disabled]="!pendingFile || store.mutating()" (click)="attachForReview()">Add document</button></div> }
742
+ @if (panel === 'correction') { <p>Create a replacement bill prefilled from this rejected submission. You can correct the payer, claim, provider, service lines, and packet before resubmitting.</p> }
743
+ @if (panel === 'close') { <p>Closing stops active follow-up. The bill history and documents remain available.</p><label><span>Reason</span><textarea [(ngModel)]="closeReason"></textarea></label> }
744
+ <footer><button type="button" (click)="panel=''">Cancel</button>@if (panel === 'payment') { <button class="primary" type="button" (click)="postPayment()" [disabled]="store.mutating() || payment.amount <= 0">Post payment</button> }@if (panel === 'review') { <button class="primary" type="button" (click)="submitReview()" [disabled]="store.mutating() || !review.reason.trim() || !review.payerClaimControlNumber.trim()">Submit Second Review</button> }@if (panel === 'correction') { <button class="primary" type="button" (click)="correct()" [disabled]="store.mutating()">Create correction</button> }@if (panel === 'close') { <button class="danger" type="button" (click)="closeBill()" [disabled]="store.mutating() || !closeReason.trim()">Close bill</button> }</footer>
745
+ </section>
746
+ </div>
747
+ }
608
748
  }
609
749
  @if (notice) { <div class="notice">{{ notice }}</div> }
610
750
  @if (store.error()) { <div class="notice error">{{ store.error()?.message }}</div> }
@@ -612,10 +752,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
612
752
  }
613
753
  <datalist id="mb-states"><option value="CA"></option><option value="AZ"></option><option value="NV"></option><option value="OR"></option><option value="WA"></option><option value="TX"></option><option value="NY"></option></datalist>
614
754
  </section>
615
- `, styles: [":host{display:block}.mb{--a:#238dbd;--ac:#fff;--bg:#f3f8fa;--s:#fff;--t:#203743;--m:#657982;--b:#dbe6ea;--r:12px;--cr:8px;display:grid;gap:16px;color:var(--t);font:14px/1.45 var(--font,Inter,system-ui,sans-serif)}*{box-sizing:border-box}h2,h3,h4,p{margin:0}.summary,.card,.delivery,.state{border:1px solid var(--b);border-radius:var(--r);background:var(--s)}.summary{display:flex;justify-content:space-between;gap:20px;padding:20px}.summary h2{font-size:24px}.summary p,.card p,.delivery p,small{color:var(--m)}.eyebrow{color:var(--m);font-size:11px;font-weight:800;letter-spacing:.13em;text-transform:uppercase}.money{text-align:right}.money span{display:block;font-size:28px;font-weight:800}.money small{font-size:12px}.review{display:grid;gap:16px}.card{padding:20px}.card-title{display:flex;align-items:start;justify-content:space-between;gap:16px;margin-bottom:18px}.card-title h3,.delivery h3{font-size:18px}.card-title>span{border-radius:999px;background:var(--bg);padding:5px 9px;color:var(--m);font-size:11px;font-weight:800;text-transform:uppercase}.grid{display:grid;gap:14px}.grid.four{grid-template-columns:repeat(4,minmax(0,1fr))}.grid.three{grid-template-columns:repeat(3,minmax(0,1fr))}.grid.two{grid-template-columns:repeat(2,minmax(0,1fr))}label{display:grid;gap:6px;color:var(--t);font-size:12px;font-weight:700}input,select,textarea,button{font:inherit}input,select,textarea{width:100%;min-height:42px;border:1px solid var(--b);border-radius:var(--cr);background:#fff;color:var(--t);padding:9px 11px}textarea{min-height:90px;resize:vertical}button{min-height:38px;border:1px solid var(--b);border-radius:var(--cr);background:#fff;color:var(--t);padding:8px 13px;cursor:pointer;font-weight:700}button.primary{border-color:var(--a);background:var(--a);color:var(--ac)}button.danger{border-color:#d4380d;background:#d4380d;color:#fff}button:disabled{cursor:not-allowed;opacity:.5}.subhead{margin:18px 0 9px;border-top:1px solid var(--b);padding-top:15px;font-size:12px;font-weight:800}.selected{display:flex;align-items:center;justify-content:space-between;border:1px solid var(--b);border-radius:var(--cr);padding:12px}.selected div{display:grid}.results{position:relative;z-index:3;display:grid;margin-top:5px;border:1px solid var(--b);border-radius:var(--cr);background:#fff;box-shadow:0 12px 30px #1f2d3d1f;overflow:hidden}.results button{display:grid;gap:2px;text-align:left;border:0;border-bottom:1px solid var(--b);border-radius:0;padding:12px}.results span{color:var(--m);font-weight:400}.presets{display:flex;gap:8px;margin:-4px 0 12px}.lines{display:grid;gap:10px}.line{display:grid;grid-template-columns:1.2fr 1.2fr 110px 120px 40px;align-items:end;gap:10px;border-radius:var(--cr);background:var(--bg);padding:12px}.line.professional{grid-template-columns:1.1fr 1fr 1fr 1fr 110px 1fr 90px 110px 40px}.allowed{display:grid;gap:7px;text-align:right}.allowed span{color:var(--m);font-size:11px;font-weight:800;text-transform:uppercase}.allowed strong{font-size:17px}.remove{border:0;background:transparent;font-size:20px}.docs{list-style:none;margin:0;padding:0}.docs li{display:flex;align-items:center;gap:8px;border-top:1px solid var(--b);padding:11px 0}.docs li>div{display:grid;flex:1}.upload{display:grid;grid-template-columns:240px 1fr auto;gap:10px;margin-top:12px;border-radius:var(--cr);background:var(--bg);padding:12px}.delivery{display:flex;align-items:center;justify-content:space-between;gap:24px;padding:20px;background:color-mix(in srgb,var(--a) 5%,white)}.actions,.actionbar{display:flex;justify-content:flex-end;gap:8px}.lifecycle{display:grid;gap:16px}.actionbar{flex-wrap:wrap}.eors{display:grid;gap:8px}.eors>button{display:flex;align-items:center;justify-content:space-between;text-align:left}.eors span{display:grid}.panel{display:grid;gap:14px;border-top:1px solid var(--b);padding-top:16px}.notice,.state{padding:13px 15px}.notice{border-radius:var(--cr);background:#edf8f2;color:#23734c}.notice.error,.state.error{background:#fff2f0;color:#b42318}.state{display:grid;gap:10px}.state button{justify-self:start}.powered{text-align:right;color:var(--m);font-size:11px}.modal-backdrop{position:fixed;z-index:1000;inset:0;display:grid;place-items:center;background:#0f20288f;padding:20px}.submit-modal{display:grid;gap:18px;width:min(680px,100%);max-height:calc(100vh - 40px);overflow:auto;border:1px solid var(--b);border-radius:var(--r);background:var(--s);padding:22px;box-shadow:0 24px 80px #0f202847}.submit-modal>header,.submit-modal>footer{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}.submit-modal>footer{justify-content:flex-end}.submit-modal .close{border:0;background:transparent;font-size:26px}.route-list{display:grid;gap:8px}.route-option{display:grid;grid-template-columns:20px 1fr auto;align-items:center;gap:12px;text-align:left;padding:13px}.route-option>span:nth-child(2){display:grid}.route-option.selected{border-color:var(--a);box-shadow:0 0 0 2px color-mix(in srgb,var(--a) 18%,transparent)}.route-option .radio{width:16px;height:16px;border:2px solid var(--b);border-radius:50%}.route-option.selected .radio{border:5px solid var(--a)}.route-option b{color:var(--a);font-size:11px;text-transform:uppercase}@media(max-width:1100px){.line.professional{grid-template-columns:repeat(3,minmax(0,1fr))}.line.professional .allowed{grid-column:auto}}@media(max-width:900px){.grid.four,.grid.three{grid-template-columns:repeat(2,minmax(0,1fr))}.line{grid-template-columns:1fr 1fr 90px}.allowed{grid-column:1/-2;text-align:left}.upload{grid-template-columns:1fr}.delivery{align-items:stretch;flex-direction:column}.actions{justify-content:flex-start}}@media(max-width:600px){.summary,.card-title{align-items:stretch;flex-direction:column}.money{text-align:left}.grid.four,.grid.three,.grid.two,.line,.line.professional{grid-template-columns:1fr}.allowed{grid-column:auto}.docs li{align-items:flex-start;flex-wrap:wrap}.modal-backdrop{padding:8px}.submit-modal{max-height:calc(100vh - 16px);padding:16px}.route-option{grid-template-columns:20px 1fr}.route-option b{grid-column:2}}\n"] }]
755
+ `, styles: [":host{display:block}.mb{--a:#238dbd;--ac:#fff;--bg:#f3f8fa;--s:#fff;--t:#203743;--m:#657982;--b:#dbe6ea;--r:12px;--cr:8px;display:grid;gap:16px;color:var(--t);font:14px/1.45 var(--font,Inter,system-ui,sans-serif)}*{box-sizing:border-box}h2,h3,h4,p{margin:0}a{color:var(--a)}.summary,.card,.delivery,.state{border:1px solid var(--b);border-radius:var(--r);background:var(--s)}.summary{display:flex;justify-content:space-between;gap:20px;padding:20px}.summary h2{font-size:24px}.summary p,.card p,.delivery p,small{color:var(--m)}.eyebrow{color:var(--m);font-size:11px;font-weight:800;letter-spacing:.13em;text-transform:uppercase}.money{text-align:right}.money span{display:block;font-size:28px;font-weight:800}.money small{font-size:12px}.review{display:grid;gap:16px}.card{padding:20px}.card-title{display:flex;align-items:start;justify-content:space-between;gap:16px;margin-bottom:18px}.card-title h3,.delivery h3{font-size:18px}.card-title>span{border-radius:999px;background:var(--bg);padding:5px 9px;color:var(--m);font-size:11px;font-weight:800;text-transform:uppercase}.grid{display:grid;gap:14px}.grid.four{grid-template-columns:repeat(4,minmax(0,1fr))}.grid.three{grid-template-columns:repeat(3,minmax(0,1fr))}.grid.two{grid-template-columns:repeat(2,minmax(0,1fr))}label{display:grid;gap:6px;color:var(--t);font-size:12px;font-weight:700}input,select,textarea,button{font:inherit}input,select,textarea{width:100%;min-height:42px;border:1px solid var(--b);border-radius:var(--cr);background:#fff;color:var(--t);padding:9px 11px}textarea{min-height:90px;resize:vertical}button{min-height:38px;border:1px solid var(--b);border-radius:var(--cr);background:#fff;color:var(--t);padding:8px 13px;cursor:pointer;font-weight:700}button.primary{border-color:var(--a);background:var(--a);color:var(--ac)}button.danger{border-color:#d4380d;background:#d4380d;color:#fff}button:disabled{cursor:not-allowed;opacity:.5}.subhead{margin:18px 0 9px;border-top:1px solid var(--b);padding-top:15px;font-size:12px;font-weight:800}.selected{display:flex;align-items:center;justify-content:space-between;border:1px solid var(--b);border-radius:var(--cr);padding:12px}.selected div{display:grid}.results{position:relative;z-index:3;display:grid;margin-top:5px;border:1px solid var(--b);border-radius:var(--cr);background:#fff;box-shadow:0 12px 30px #1f2d3d1f;overflow:hidden}.results button{display:grid;gap:2px;text-align:left;border:0;border-bottom:1px solid var(--b);border-radius:0;padding:12px}.results span{color:var(--m);font-weight:400}.presets{display:flex;gap:8px;margin:-4px 0 12px}.lines{display:grid;gap:10px}.line{display:grid;grid-template-columns:1.2fr 1.2fr 110px 120px 40px;align-items:end;gap:10px;border-radius:var(--cr);background:var(--bg);padding:12px}.line.professional{grid-template-columns:1.1fr 1fr 1fr 1fr 110px 1fr 90px 110px 40px}.allowed{display:grid;gap:7px;text-align:right}.allowed span{color:var(--m);font-size:11px;font-weight:800;text-transform:uppercase}.allowed strong{font-size:17px}.remove{border:0;background:transparent;font-size:20px}.docs{list-style:none;margin:0;padding:0}.docs li{display:flex;align-items:center;gap:8px;border-top:1px solid var(--b);padding:11px 0}.docs li>div{display:grid;flex:1}.upload{display:grid;grid-template-columns:240px 1fr auto;gap:10px;margin-top:12px;border-radius:var(--cr);background:var(--bg);padding:12px}.delivery{display:flex;align-items:center;justify-content:space-between;gap:24px;padding:20px;background:color-mix(in srgb,var(--a) 5%,white)}.actions,.actionbar{display:flex;justify-content:flex-end;gap:8px}.progress ol{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));list-style:none;margin:0;padding:0}.progress li{position:relative;display:grid;justify-items:center;gap:7px;color:var(--m);font-size:12px;text-align:center}.progress li:before{content:\"\";position:absolute;top:14px;right:50%;left:-50%;height:2px;background:var(--b)}.progress li:first-child:before{display:none}.progress li b{position:relative;z-index:1;display:grid;place-items:center;width:28px;height:28px;border:2px solid var(--b);border-radius:50%;background:var(--s)}.progress li.complete,.progress li.current{color:var(--t)}.progress li.complete:before,.progress li.current:before{background:var(--a)}.progress li.complete b,.progress li.current b{border-color:var(--a);background:var(--a);color:var(--ac)}.snapshot dl,.facts{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));margin:0}.snapshot dl>div,.facts>div{display:grid;gap:3px;border-right:1px solid var(--b);padding:4px 14px}.snapshot dl>div:nth-child(3n),.facts>div:last-child{border-right:0}.snapshot dt,.facts dt,.contacts dt{color:var(--m);font-size:11px;font-weight:800;text-transform:uppercase}.snapshot dd,.facts dd,.contacts dd{margin:0;font-weight:700}.lifecycle{display:grid;gap:20px}.actionbar{flex-wrap:wrap;border-top:1px solid var(--b);padding-top:18px}.lifecycle-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px}.lifecycle-grid>section,.payments,.history,.eors{display:grid;gap:11px;border:1px solid var(--b);border-radius:var(--cr);padding:15px}.contacts{display:grid;gap:9px;margin:0}.contacts>div{display:grid;grid-template-columns:70px 1fr;gap:10px}.denial{border-radius:var(--cr);background:#fff4ed;padding:11px}.eors>button{display:flex;align-items:center;justify-content:space-between;text-align:left}.eors span{display:grid}.payments article{display:grid;grid-template-columns:1fr auto;gap:4px;border-top:1px solid var(--b);padding-top:10px}.payments article>div{display:grid}.payments article>div:last-of-type{text-align:right}.payments article p{grid-column:1/-1}.history ol{display:grid;gap:0;list-style:none;margin:0;padding:0}.history li{display:grid;grid-template-columns:18px 1fr;gap:9px}.history li>span{position:relative;margin-top:5px;width:10px;height:10px;border-radius:50%;background:var(--a)}.history li>span:after{content:\"\";position:absolute;top:10px;bottom:-30px;left:4px;width:2px;background:var(--b)}.history li:last-child>span:after{display:none}.history li>div{display:grid;gap:2px;padding-bottom:15px}.notice,.state{padding:13px 15px}.notice{border-radius:var(--cr);background:#edf8f2;color:#23734c}.notice.error,.state.error{background:#fff2f0;color:#b42318}.state{display:grid;gap:10px}.state button{justify-self:start}.powered{text-align:right;color:var(--m);font-size:11px}.modal-backdrop{position:fixed;z-index:1000;inset:0;display:grid;place-items:center;background:#0f20288f;padding:20px}.submit-modal{display:grid;gap:18px;width:min(680px,100%);max-height:calc(100vh - 40px);overflow:auto;border:1px solid var(--b);border-radius:var(--r);background:var(--s);padding:22px;box-shadow:0 24px 80px #0f202847}.submit-modal>header,.submit-modal>footer{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}.submit-modal>footer{justify-content:flex-end}.submit-modal .close{border:0;background:transparent;font-size:26px}.route-list{display:grid;gap:8px}.route-option{display:grid;grid-template-columns:20px 1fr auto;align-items:center;gap:12px;text-align:left;padding:13px}.route-option>span:nth-child(2){display:grid}.route-option.selected{border-color:var(--a);box-shadow:0 0 0 2px color-mix(in srgb,var(--a) 18%,transparent)}.route-option .radio{width:16px;height:16px;border:2px solid var(--b);border-radius:50%}.route-option.selected .radio{border:5px solid var(--a)}.route-option b{color:var(--a);font-size:11px;text-transform:uppercase}.action-modal{width:min(720px,100%)}.review-docs{display:grid;gap:8px;border:1px solid var(--b);border-radius:var(--cr);padding:12px}.review-docs label{display:grid;grid-template-columns:20px 1fr;align-items:start}.review-docs input{min-height:auto}.review-docs label>span{display:grid}@media(max-width:1100px){.line.professional{grid-template-columns:repeat(3,minmax(0,1fr))}.line.professional .allowed{grid-column:auto}}@media(max-width:900px){.grid.four,.grid.three{grid-template-columns:repeat(2,minmax(0,1fr))}.line{grid-template-columns:1fr 1fr 90px}.allowed{grid-column:1/-2;text-align:left}.upload{grid-template-columns:1fr}.delivery{align-items:stretch;flex-direction:column}.actions{justify-content:flex-start}.lifecycle-grid{grid-template-columns:1fr}}@media(max-width:600px){.summary,.card-title{align-items:stretch;flex-direction:column}.money{text-align:left}.grid.four,.grid.three,.grid.two,.line,.line.professional{grid-template-columns:1fr}.allowed{grid-column:auto}.docs li{align-items:flex-start;flex-wrap:wrap}.progress{overflow-x:auto}.progress ol{min-width:560px}.snapshot dl,.facts{grid-template-columns:1fr}.snapshot dl>div,.facts>div{border-right:0;border-bottom:1px solid var(--b);padding:10px 0}.snapshot dl>div:last-child,.facts>div:last-child{border-bottom:0}.modal-backdrop{align-items:end;padding:0}.submit-modal{max-height:92vh;border-radius:var(--r) var(--r) 0 0;padding:16px}.route-option{grid-template-columns:20px 1fr}.route-option b{grid-column:2}}\n"] }]
616
756
  }], ctorParameters: () => [], propDecorators: { billId: [{
617
- type: Input,
618
- args: [{ required: true }]
757
+ type: Input
758
+ }], create: [{
759
+ type: Input
619
760
  }], sessionEndpoint: [{
620
761
  type: Input
621
762
  }], apiBaseUrl: [{
@@ -628,6 +769,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
628
769
  type: Input
629
770
  }], billIdChange: [{
630
771
  type: Output
772
+ }], billCreated: [{
773
+ type: Output
631
774
  }], submitted: [{
632
775
  type: Output
633
776
  }], billingError: [{