@mindbill/angular 0.1.2 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 IncidentFox, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # @mindbill/angular
2
+
3
+ Native Angular billing UI with built-in session renewal and lifecycle API calls.
4
+
5
+ ```bash
6
+ npm install @mindbill/angular @mindbill/node
7
+ ```
8
+
9
+ Import the standalone component and give it the one value your application keeps: the bill ID.
10
+
11
+ ```ts
12
+ import { Component } from "@angular/core";
13
+ import { MindBillBillLifecycleComponent } from "@mindbill/angular";
14
+
15
+ @Component({
16
+ selector: "app-case-billing",
17
+ standalone: true,
18
+ imports: [MindBillBillLifecycleComponent],
19
+ template: `
20
+ <mindbill-bill-lifecycle
21
+ [billId]="billId"
22
+ sessionEndpoint="/api/mindbill/bill-session"
23
+ [appearance]="{ preset: 'clinical-blue' }"
24
+ (billIdChange)="billId = $event"
25
+ />
26
+ `,
27
+ })
28
+ export class CaseBillingComponent {
29
+ billId = "bill_123";
30
+ }
31
+ ```
32
+
33
+ The component loads and refreshes status, searches the payer directory, saves bill edits, manages the explicit payer packet, submits the bill, shows EORs, and exposes the correct payment, review, correction, resubmission, and close actions for the current state.
34
+
35
+ 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
+ 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
+
39
+ ```ts
40
+ import { MindBillClient } from "@mindbill/node";
41
+
42
+ const mindbill = new MindBillClient({ apiKey: process.env["MINDBILL_API_KEY"]! });
43
+
44
+ app.post("/api/mindbill/bill-session", requireUser, async (req, res) => {
45
+ await requireBillAccess(req.user, req.body.billId);
46
+ const session = await mindbill.createBrowserSession({
47
+ component: "bill-review",
48
+ billId: req.body.billId,
49
+ allowedOrigin: `${req.protocol}://${req.get("host")}`,
50
+ expiresIn: 900,
51
+ });
52
+ res.json(session);
53
+ });
54
+ ```
55
+
56
+ Your permanent API key never reaches Angular. Available presets are `mindbill`, `qme-companion`, `orange-bright`, and `clinical-blue`; every visual token can also be overridden.
@@ -52,8 +52,9 @@ class MindBillLifecycleStore {
52
52
  searchClaimsAdministrators(query, claimNumber) {
53
53
  return this.requireClient().searchClaimsAdministrators(query, claimNumber);
54
54
  }
55
+ getDeliveryOptions() { return this.requireClient().getDeliveryOptions(); }
55
56
  saveReview(input) { return this.mutate(() => this.requireClient().saveReview(input)); }
56
- submitBill(input, route) { return this.mutate(() => this.requireClient().submitBill(input, route)); }
57
+ submitBill(input, submission) { return this.mutate(() => this.requireClient().submitBill(input, submission)); }
57
58
  addAttachment(file, type, description) { return this.mutate(() => this.requireClient().addAttachment(file, type, description)); }
58
59
  removeAttachment(id) { return this.mutate(() => this.requireClient().removeAttachment(id)); }
59
60
  getAttachment(id) { return this.requireClient().getAttachment(id); }
@@ -110,7 +111,13 @@ const EMPTY_PROCEDURE_LINE = {
110
111
  charge: 0,
111
112
  };
112
113
  function isEmptyProcedureLine(line) {
113
- return !line.code.trim() && line.modifiers.length === 0 && line.units === 1;
114
+ return !line.code.trim()
115
+ && line.modifiers.length === 0
116
+ && line.units === 1
117
+ && !line.serviceDate
118
+ && !line.serviceDateEnd
119
+ && !(line.diagnosisPointers?.length)
120
+ && !line.charge;
114
121
  }
115
122
  /** Keeps completed/partial lines plus one keyboard-ready empty row. */
116
123
  function ensureTrailingProcedureLine(lines) {
@@ -133,6 +140,7 @@ const DOCUMENT_TYPES = [
133
140
  { value: "proof_of_service", label: "Proof of service" },
134
141
  { value: "letter_of_attestation", label: "Letter of attestation" },
135
142
  { value: "form_122", label: "Required form" },
143
+ { value: "return_to_work_voucher", label: "Return-to-work voucher" },
136
144
  { value: "w9", label: "W-9" },
137
145
  { value: "appeal", label: "Appeal support" },
138
146
  { value: "medical_records", label: "Medical records (intentional)" },
@@ -150,14 +158,17 @@ class MindBillBillLifecycleComponent {
150
158
  billingError = new EventEmitter();
151
159
  store = new MindBillLifecycleStore();
152
160
  documentTypes = DOCUMENT_TYPES;
153
- routes = [{ value: "ebill", label: "E-bill" }, { value: "fax", label: "Fax" }, { value: "mail", label: "Mail" }, { value: "email", label: "Email" }];
161
+ medLegalCodes = ["ML200", "ML201", "ML202", "ML203", "ML204", "ML205", "ML206", "MLPRR"];
154
162
  draft = null;
155
163
  dirty = false;
156
164
  payerQuery = "";
157
165
  payerResults = [];
158
166
  documentType = "other";
159
167
  pendingFile = null;
160
- route = "ebill";
168
+ deliveryOptions = null;
169
+ submission = { route: "ebill" };
170
+ showSubmit = false;
171
+ deliveryBusy = false;
161
172
  panel = "";
162
173
  notice = "";
163
174
  closeReason = "";
@@ -213,17 +224,68 @@ class MindBillBillLifecycleComponent {
213
224
  lineChanged() { this.normalizeLineItems(); this.dirty = true; }
214
225
  removeLine(index) { this.draft?.bill.lineItems.splice(index, 1); this.normalizeLineItems(); this.dirty = true; }
215
226
  setModifiers(line, value) { line.modifiers = value.split(",").map((item) => item.trim().replace(/^-/, "")).filter(Boolean); this.normalizeLineItems(); this.dirty = true; }
227
+ setDiagnosisPointers(line, value) { line.diagnosisPointers = value.split(",").map((item) => Number(item.trim())).filter((item) => Number.isInteger(item) && item >= 1 && item <= 12); this.normalizeLineItems(); this.dirty = true; }
228
+ applyPreset(kind) {
229
+ if (!this.draft)
230
+ return;
231
+ const modifier = kind === "ame" ? "94" : kind === "psych" ? "96" : "95";
232
+ const entered = this.draft.bill.lineItems.filter((line) => line.code.trim());
233
+ if (entered.length === 0)
234
+ entered.push({ code: "ML201", modifiers: [modifier], units: 1, charge: 0 });
235
+ else
236
+ entered.forEach((line) => { if (!line.modifiers.length)
237
+ line.modifiers = [modifier]; });
238
+ this.draft.bill.lineItems = ensureTrailingProcedureLine(entered);
239
+ this.dirty = true;
240
+ }
216
241
  fileSelected(event) { this.pendingFile = event.target.files?.[0] ?? null; }
217
242
  async attach() { if (!this.pendingFile)
218
243
  return; await this.store.addAttachment(this.pendingFile, this.documentType); this.pendingFile = null; this.notice = "Document attached."; }
219
244
  async removeAttachment(id) { await this.store.removeAttachment(id); this.notice = "Document removed."; }
220
245
  async openAttachment(id, filename) { this.openBlob(await this.store.getAttachment(id), filename); }
221
246
  async openEor(id, filename) { this.openBlob(await this.store.getEor(id), filename); }
222
- canSubmit() { const input = this.buildInput(); return Boolean(input && input.claimsAdminId && input.dos && input.billingProvider?.name && input.billingProvider.taxId && input.billingProvider.npi && input.renderingProvider?.name && input.renderingProvider.npi && input.lineItems.some((line) => line.code && line.units > 0)); }
247
+ canSubmit() {
248
+ const input = this.buildInput();
249
+ if (!input || !input.claimsAdminId || !input.billingProvider?.name || !input.billingProvider.taxId || !input.billingProvider.npi || !input.renderingProvider?.name || !input.renderingProvider.npi || !input.lineItems.length)
250
+ return false;
251
+ return input.lineItems.every((line) => line.code && line.units > 0 && (this.draft?.bill.billingMode !== "professional" || Boolean(line.serviceDate && line.charge && line.charge > 0)));
252
+ }
223
253
  async save() { const input = this.buildInput(); if (!input)
224
254
  return; const data = await this.store.saveReview(input); this.dirty = false; this.draft = this.makeDraft(data); this.notice = "Bill saved."; }
225
- async submit() { const input = this.buildInput(); if (!input || !this.canSubmit())
226
- return; const data = await this.store.submitBill(input, this.route); this.dirty = false; this.draft = this.makeDraft(data); this.notice = "Bill submitted."; this.submitted.emit(data); }
255
+ async openSubmit() {
256
+ if (!this.canSubmit())
257
+ return;
258
+ this.deliveryBusy = true;
259
+ try {
260
+ this.deliveryOptions = await this.store.getDeliveryOptions();
261
+ this.chooseDelivery(this.deliveryOptions.recommended);
262
+ this.showSubmit = true;
263
+ }
264
+ finally {
265
+ this.deliveryBusy = false;
266
+ }
267
+ }
268
+ closeSubmit() { this.showSubmit = false; }
269
+ selectedDelivery() { return this.deliveryOptions?.options.find((option) => option.route === this.submission.route); }
270
+ chooseDelivery(option) {
271
+ const destination = option.route === "fax" ? { faxNumber: option.target ?? this.deliveryOptions?.contacts.faxNumber ?? "" }
272
+ : option.route === "email" ? { email: option.target ?? this.deliveryOptions?.contacts.claimsEmail ?? "" }
273
+ : option.route === "mail" ? { mailingAddress: option.target ?? this.deliveryOptions?.contacts.mailingAddress ?? "" }
274
+ : undefined;
275
+ this.submission = { route: option.route, ...(destination ? { destination } : {}) };
276
+ }
277
+ setDestination(key, value) { this.submission = { ...this.submission, destination: { ...this.submission.destination, [key]: value } }; }
278
+ deliveryReady() {
279
+ if (this.submission.route === "fax")
280
+ return Boolean(this.submission.destination?.faxNumber?.trim());
281
+ if (this.submission.route === "email")
282
+ return Boolean(this.submission.destination?.email?.trim());
283
+ if (this.submission.route === "mail")
284
+ return Boolean(this.submission.destination?.mailingAddress?.trim());
285
+ return true;
286
+ }
287
+ async submit() { const input = this.buildInput(); if (!input || !this.canSubmit() || !this.deliveryReady())
288
+ 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); }
227
289
  beginAction(action) { if (action === "post_payment")
228
290
  this.panel = "payment";
229
291
  else if (action === "second_review" || action === "independent_bill_review")
@@ -270,6 +332,10 @@ class MindBillBillLifecycleComponent {
270
332
  code: line.code,
271
333
  modifiers: line.modifiers,
272
334
  units: line.units,
335
+ ...(line.charge > 0 ? { charge: line.charge } : {}),
336
+ ...(line.serviceDate ? { serviceDate: line.serviceDate } : {}),
337
+ ...(line.serviceDateEnd ? { serviceDateEnd: line.serviceDateEnd } : {}),
338
+ ...(line.diagnosisPointers?.length ? { diagnosisPointers: line.diagnosisPointers } : {}),
273
339
  })),
274
340
  };
275
341
  }
@@ -345,8 +411,29 @@ class MindBillBillLifecycleComponent {
345
411
  </section>
346
412
 
347
413
  <section class="card">
348
- <div class="card-title"><div><h3>Charges</h3><p>Review procedure codes, modifiers, units, and allowed amounts. A new row appears as you type.</p></div></div>
349
- <div class="lines">@for (line of draft.bill.lineItems; track $index; let i=$index) { <div class="line"><label><span>Procedure</span><input required [(ngModel)]="line.code" [name]="'code'+i" (ngModelChange)="lineChanged()"></label><label><span>Modifiers</span><input [ngModel]="line.modifiers.join(', ')" (ngModelChange)="setModifiers(line,$any($event))" [name]="'modifiers'+i" placeholder="95, 93"></label><label><span>Units</span><input required type="number" min="1" [(ngModel)]="line.units" [name]="'units'+i" (ngModelChange)="lineChanged()"></label><div class="allowed"><span>Allowed</span><strong>{{ line.charge | currency }}</strong></div><button type="button" class="remove" (click)="removeLine(i)" aria-label="Remove line">×</button></div> }</div>
414
+ <div class="card-title"><div><h3>Charges</h3><p>{{ draft.bill.billingMode === 'professional' ? 'Enter each treatment or professional service.' : 'Choose medical-legal codes, modifiers, units, and allowed amounts.' }} A keyboard-ready row appears automatically.</p></div></div>
415
+ @if (draft.bill.billingMode === 'med_legal') {
416
+ <div class="presets"><button type="button" (click)="applyPreset('qme')">QME</button><button type="button" (click)="applyPreset('ame')">AME</button><button type="button" (click)="applyPreset('psych')">Psych QME</button></div>
417
+ }
418
+ <div class="lines">@for (line of draft.bill.lineItems; track $index; let i=$index) {
419
+ <div class="line" [class.professional]="draft.bill.billingMode === 'professional'">
420
+ <label><span>Procedure</span>
421
+ @if (draft.bill.billingMode === 'med_legal') {
422
+ <select [(ngModel)]="line.code" [name]="'code'+i" (ngModelChange)="lineChanged()"><option value="">Choose code…</option>@for (code of medLegalCodes; track code) { <option [value]="code">{{ code }}</option> }</select>
423
+ } @else { <input [(ngModel)]="line.code" [name]="'code'+i" (ngModelChange)="lineChanged()" placeholder="e.g. 99204"> }
424
+ </label>
425
+ <label><span>Modifiers</span><input [ngModel]="line.modifiers.join(', ')" (ngModelChange)="setModifiers(line,$any($event))" [name]="'modifiers'+i" placeholder="e.g. 25, 95"></label>
426
+ @if (draft.bill.billingMode === 'professional') {
427
+ <label><span>Service date</span><input type="date" [(ngModel)]="line.serviceDate" [name]="'serviceDate'+i" (ngModelChange)="lineChanged()"></label>
428
+ <label><span>End date</span><input type="date" [(ngModel)]="line.serviceDateEnd" [name]="'serviceDateEnd'+i" (ngModelChange)="lineChanged()"></label>
429
+ <label><span>Charge</span><input type="number" min="0" step="0.01" [(ngModel)]="line.charge" [name]="'charge'+i" (ngModelChange)="lineChanged()"></label>
430
+ <label><span>Diagnosis pointers</span><input [ngModel]="(line.diagnosisPointers || []).join(', ')" (ngModelChange)="setDiagnosisPointers(line,$any($event))" [name]="'pointers'+i" placeholder="1, 2"></label>
431
+ }
432
+ <label><span>Units</span><input type="number" min="1" [(ngModel)]="line.units" [name]="'units'+i" (ngModelChange)="lineChanged()"></label>
433
+ <div class="allowed"><span>{{ draft.bill.billingMode === 'professional' ? 'Charge' : 'Allowed' }}</span><strong>{{ line.charge | currency }}</strong></div>
434
+ <button type="button" class="remove" (click)="removeLine(i)" aria-label="Remove line">×</button>
435
+ </div>
436
+ }</div>
350
437
  </section>
351
438
 
352
439
  <section class="card">
@@ -355,7 +442,25 @@ class MindBillBillLifecycleComponent {
355
442
  <div class="upload"><select [(ngModel)]="documentType" name="documentType">@for (type of documentTypes; track type.value) { <option [value]="type.value">{{ type.label }}</option> }</select><input #fileInput type="file" accept="application/pdf" (change)="fileSelected($event)"><button type="button" [disabled]="!pendingFile || store.mutating()" (click)="attach()">Attach document</button></div>
356
443
  </section>
357
444
 
358
- <section class="delivery"><div><span class="eyebrow">Delivery</span><h3>Submit this bill</h3><p>Submission and every later action remain available in this case.</p></div><div><div class="routes">@for (item of routes; track item.value) { <label><input type="radio" name="route" [value]="item.value" [(ngModel)]="route"> {{ item.label }}</label> }</div><div class="actions"><button type="button" (click)="save()" [disabled]="store.mutating()">Save changes</button><button class="primary" type="submit" [disabled]="store.mutating() || !canSubmit()">{{ store.mutating() ? 'Submitting…' : 'Submit bill' }}</button></div></div></section>
445
+ <section class="delivery"><div><span class="eyebrow">Delivery</span><h3>Submit this bill</h3><p>Review the exact electronic, fax, email, or mail destination before anything is sent.</p></div><div class="actions"><button type="button" (click)="save()" [disabled]="store.mutating()">Save changes</button><button class="primary" type="button" (click)="openSubmit()" [disabled]="store.mutating() || deliveryBusy || !canSubmit()">{{ deliveryBusy ? 'Loading routes…' : 'Review delivery' }}</button></div></section>
446
+
447
+ @if (showSubmit && deliveryOptions) {
448
+ <div class="modal-backdrop" (click)="closeSubmit()">
449
+ <section class="submit-modal" role="dialog" aria-modal="true" aria-labelledby="mb-submit-title" (click)="$event.stopPropagation()">
450
+ <header><div><span class="eyebrow">Send bill</span><h3 id="mb-submit-title">Choose a delivery route</h3><p>{{ deliveryOptions.payerName }}</p></div><button type="button" class="close" (click)="closeSubmit()" aria-label="Close">×</button></header>
451
+ <div class="route-list">@for (option of deliveryOptions.options; track option.route) {
452
+ <button type="button" class="route-option" [class.selected]="submission.route === option.route" (click)="chooseDelivery(option)"><span class="radio"></span><span><strong>{{ option.label }}</strong><small>{{ option.detail }}</small></span>@if (option.route === deliveryOptions.recommended.route) { <b>Recommended</b> }</button>
453
+ }</div>
454
+ @if (selectedDelivery(); as option) {
455
+ @if (option.route === 'fax') { <label><span>Fax number</span><input [ngModel]="submission.destination?.faxNumber || ''" (ngModelChange)="setDestination('faxNumber',$any($event))" name="submitFax"></label> }
456
+ @if (option.route === 'email') { <label><span>Email address</span><input type="email" [ngModel]="submission.destination?.email || ''" (ngModelChange)="setDestination('email',$any($event))" name="submitEmail"></label><label><span>Subject</span><input [(ngModel)]="submission.subject" name="submitSubject"></label> }
457
+ @if (option.route === 'mail') { <label><span>Mailing address</span><textarea [ngModel]="submission.destination?.mailingAddress || ''" (ngModelChange)="setDestination('mailingAddress',$any($event))" name="submitMail"></textarea></label> }
458
+ @if (option.route !== 'ebill') { <label><span>Attention (optional)</span><input [(ngModel)]="submission.attention" name="submitAttention"></label> }
459
+ }
460
+ <footer><button type="button" (click)="closeSubmit()">Cancel</button><button type="button" class="primary" (click)="submit()" [disabled]="store.mutating() || !deliveryReady()">{{ store.mutating() ? 'Submitting…' : 'Submit bill' }}</button></footer>
461
+ </section>
462
+ </div>
463
+ }
359
464
  </form>
360
465
  } @else {
361
466
  <section class="card lifecycle">
@@ -373,7 +478,7 @@ class MindBillBillLifecycleComponent {
373
478
  }
374
479
  <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>
375
480
  </section>
376
- `, 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}.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}.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)}.routes,.actions,.actionbar{display:flex;justify-content:flex-end;gap:8px}.routes{margin-bottom:10px}.routes label{display:flex;align-items:center;border:1px solid var(--b);border-radius:var(--cr);background:#fff;padding:9px 12px}.routes input{width:auto;min-height:auto}.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}@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}.routes,.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{grid-template-columns:1fr}.allowed{grid-column:auto}.routes{flex-wrap:wrap}.docs li{align-items:flex-start;flex-wrap:wrap}}\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.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { 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" }] });
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" }] });
377
482
  }
378
483
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: MindBillBillLifecycleComponent, decorators: [{
379
484
  type: Component,
@@ -440,8 +545,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
440
545
  </section>
441
546
 
442
547
  <section class="card">
443
- <div class="card-title"><div><h3>Charges</h3><p>Review procedure codes, modifiers, units, and allowed amounts. A new row appears as you type.</p></div></div>
444
- <div class="lines">@for (line of draft.bill.lineItems; track $index; let i=$index) { <div class="line"><label><span>Procedure</span><input required [(ngModel)]="line.code" [name]="'code'+i" (ngModelChange)="lineChanged()"></label><label><span>Modifiers</span><input [ngModel]="line.modifiers.join(', ')" (ngModelChange)="setModifiers(line,$any($event))" [name]="'modifiers'+i" placeholder="95, 93"></label><label><span>Units</span><input required type="number" min="1" [(ngModel)]="line.units" [name]="'units'+i" (ngModelChange)="lineChanged()"></label><div class="allowed"><span>Allowed</span><strong>{{ line.charge | currency }}</strong></div><button type="button" class="remove" (click)="removeLine(i)" aria-label="Remove line">×</button></div> }</div>
548
+ <div class="card-title"><div><h3>Charges</h3><p>{{ draft.bill.billingMode === 'professional' ? 'Enter each treatment or professional service.' : 'Choose medical-legal codes, modifiers, units, and allowed amounts.' }} A keyboard-ready row appears automatically.</p></div></div>
549
+ @if (draft.bill.billingMode === 'med_legal') {
550
+ <div class="presets"><button type="button" (click)="applyPreset('qme')">QME</button><button type="button" (click)="applyPreset('ame')">AME</button><button type="button" (click)="applyPreset('psych')">Psych QME</button></div>
551
+ }
552
+ <div class="lines">@for (line of draft.bill.lineItems; track $index; let i=$index) {
553
+ <div class="line" [class.professional]="draft.bill.billingMode === 'professional'">
554
+ <label><span>Procedure</span>
555
+ @if (draft.bill.billingMode === 'med_legal') {
556
+ <select [(ngModel)]="line.code" [name]="'code'+i" (ngModelChange)="lineChanged()"><option value="">Choose code…</option>@for (code of medLegalCodes; track code) { <option [value]="code">{{ code }}</option> }</select>
557
+ } @else { <input [(ngModel)]="line.code" [name]="'code'+i" (ngModelChange)="lineChanged()" placeholder="e.g. 99204"> }
558
+ </label>
559
+ <label><span>Modifiers</span><input [ngModel]="line.modifiers.join(', ')" (ngModelChange)="setModifiers(line,$any($event))" [name]="'modifiers'+i" placeholder="e.g. 25, 95"></label>
560
+ @if (draft.bill.billingMode === 'professional') {
561
+ <label><span>Service date</span><input type="date" [(ngModel)]="line.serviceDate" [name]="'serviceDate'+i" (ngModelChange)="lineChanged()"></label>
562
+ <label><span>End date</span><input type="date" [(ngModel)]="line.serviceDateEnd" [name]="'serviceDateEnd'+i" (ngModelChange)="lineChanged()"></label>
563
+ <label><span>Charge</span><input type="number" min="0" step="0.01" [(ngModel)]="line.charge" [name]="'charge'+i" (ngModelChange)="lineChanged()"></label>
564
+ <label><span>Diagnosis pointers</span><input [ngModel]="(line.diagnosisPointers || []).join(', ')" (ngModelChange)="setDiagnosisPointers(line,$any($event))" [name]="'pointers'+i" placeholder="1, 2"></label>
565
+ }
566
+ <label><span>Units</span><input type="number" min="1" [(ngModel)]="line.units" [name]="'units'+i" (ngModelChange)="lineChanged()"></label>
567
+ <div class="allowed"><span>{{ draft.bill.billingMode === 'professional' ? 'Charge' : 'Allowed' }}</span><strong>{{ line.charge | currency }}</strong></div>
568
+ <button type="button" class="remove" (click)="removeLine(i)" aria-label="Remove line">×</button>
569
+ </div>
570
+ }</div>
445
571
  </section>
446
572
 
447
573
  <section class="card">
@@ -450,7 +576,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
450
576
  <div class="upload"><select [(ngModel)]="documentType" name="documentType">@for (type of documentTypes; track type.value) { <option [value]="type.value">{{ type.label }}</option> }</select><input #fileInput type="file" accept="application/pdf" (change)="fileSelected($event)"><button type="button" [disabled]="!pendingFile || store.mutating()" (click)="attach()">Attach document</button></div>
451
577
  </section>
452
578
 
453
- <section class="delivery"><div><span class="eyebrow">Delivery</span><h3>Submit this bill</h3><p>Submission and every later action remain available in this case.</p></div><div><div class="routes">@for (item of routes; track item.value) { <label><input type="radio" name="route" [value]="item.value" [(ngModel)]="route"> {{ item.label }}</label> }</div><div class="actions"><button type="button" (click)="save()" [disabled]="store.mutating()">Save changes</button><button class="primary" type="submit" [disabled]="store.mutating() || !canSubmit()">{{ store.mutating() ? 'Submitting…' : 'Submit bill' }}</button></div></div></section>
579
+ <section class="delivery"><div><span class="eyebrow">Delivery</span><h3>Submit this bill</h3><p>Review the exact electronic, fax, email, or mail destination before anything is sent.</p></div><div class="actions"><button type="button" (click)="save()" [disabled]="store.mutating()">Save changes</button><button class="primary" type="button" (click)="openSubmit()" [disabled]="store.mutating() || deliveryBusy || !canSubmit()">{{ deliveryBusy ? 'Loading routes…' : 'Review delivery' }}</button></div></section>
580
+
581
+ @if (showSubmit && deliveryOptions) {
582
+ <div class="modal-backdrop" (click)="closeSubmit()">
583
+ <section class="submit-modal" role="dialog" aria-modal="true" aria-labelledby="mb-submit-title" (click)="$event.stopPropagation()">
584
+ <header><div><span class="eyebrow">Send bill</span><h3 id="mb-submit-title">Choose a delivery route</h3><p>{{ deliveryOptions.payerName }}</p></div><button type="button" class="close" (click)="closeSubmit()" aria-label="Close">×</button></header>
585
+ <div class="route-list">@for (option of deliveryOptions.options; track option.route) {
586
+ <button type="button" class="route-option" [class.selected]="submission.route === option.route" (click)="chooseDelivery(option)"><span class="radio"></span><span><strong>{{ option.label }}</strong><small>{{ option.detail }}</small></span>@if (option.route === deliveryOptions.recommended.route) { <b>Recommended</b> }</button>
587
+ }</div>
588
+ @if (selectedDelivery(); as option) {
589
+ @if (option.route === 'fax') { <label><span>Fax number</span><input [ngModel]="submission.destination?.faxNumber || ''" (ngModelChange)="setDestination('faxNumber',$any($event))" name="submitFax"></label> }
590
+ @if (option.route === 'email') { <label><span>Email address</span><input type="email" [ngModel]="submission.destination?.email || ''" (ngModelChange)="setDestination('email',$any($event))" name="submitEmail"></label><label><span>Subject</span><input [(ngModel)]="submission.subject" name="submitSubject"></label> }
591
+ @if (option.route === 'mail') { <label><span>Mailing address</span><textarea [ngModel]="submission.destination?.mailingAddress || ''" (ngModelChange)="setDestination('mailingAddress',$any($event))" name="submitMail"></textarea></label> }
592
+ @if (option.route !== 'ebill') { <label><span>Attention (optional)</span><input [(ngModel)]="submission.attention" name="submitAttention"></label> }
593
+ }
594
+ <footer><button type="button" (click)="closeSubmit()">Cancel</button><button type="button" class="primary" (click)="submit()" [disabled]="store.mutating() || !deliveryReady()">{{ store.mutating() ? 'Submitting…' : 'Submit bill' }}</button></footer>
595
+ </section>
596
+ </div>
597
+ }
454
598
  </form>
455
599
  } @else {
456
600
  <section class="card lifecycle">
@@ -468,7 +612,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
468
612
  }
469
613
  <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>
470
614
  </section>
471
- `, 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}.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}.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)}.routes,.actions,.actionbar{display:flex;justify-content:flex-end;gap:8px}.routes{margin-bottom:10px}.routes label{display:flex;align-items:center;border:1px solid var(--b);border-radius:var(--cr);background:#fff;padding:9px 12px}.routes input{width:auto;min-height:auto}.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}@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}.routes,.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{grid-template-columns:1fr}.allowed{grid-column:auto}.routes{flex-wrap:wrap}.docs li{align-items:flex-start;flex-wrap:wrap}}\n"] }]
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"] }]
472
616
  }], ctorParameters: () => [], propDecorators: { billId: [{
473
617
  type: Input,
474
618
  args: [{ required: true }]
@@ -494,5 +638,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
494
638
  * Generated bundle index. Do not edit.
495
639
  */
496
640
 
497
- export { MindBillBillLifecycleComponent, MindBillLifecycleStore, ensureTrailingProcedureLine };
641
+ export { MindBillBillLifecycleComponent, MindBillLifecycleStore, ensureTrailingProcedureLine, isEmptyProcedureLine };
498
642
  //# sourceMappingURL=mindbill-angular.mjs.map