@mindbill/angular 0.9.0 → 0.11.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 +26 -0
- package/fesm2022/mindbill-angular.mjs +216 -19
- package/fesm2022/mindbill-angular.mjs.map +1 -1
- package/package.json +2 -2
- package/types/mindbill-angular.d.ts +35 -8
package/README.md
CHANGED
|
@@ -55,6 +55,32 @@ export class CaseBillingComponent {
|
|
|
55
55
|
|
|
56
56
|
`MindBillBillSubmissionComponent` marks required fields, scrolls to invalid input, resolves ZIP codes to city and state with an inline status line, searches the MindBill claims-administrator and ICD-10 directories as you type, calculates service-line totals, and uploads attachments. Dates use a masked MM/DD/YYYY input that accepts ISO, US, or bare-digit entry, normalizes the display once a complete date is typed, and submits ISO values — identical to the React form. On success, render `MindBillBillLifecycleComponent` with the returned bill ID. The lifecycle component loads its own immutable bill snapshot, status, EORs, payments, history, and available actions.
|
|
57
57
|
|
|
58
|
+
When the lifecycle is rejected, it automatically renders the full ordered list of clearinghouse issues with actionable descriptions, acknowledgement codes, and sent/rejected timestamps. For custom layouts, use the same standalone surface directly:
|
|
59
|
+
|
|
60
|
+
```html
|
|
61
|
+
<mindbill-bill-rejection-notice
|
|
62
|
+
[rejection]="rejection"
|
|
63
|
+
[submittedAt]="submittedAt"
|
|
64
|
+
[appearance]="{ preset: 'mindbill' }"
|
|
65
|
+
/>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { MindBillBillRejectionNoticeComponent } from "@mindbill/angular";
|
|
70
|
+
|
|
71
|
+
rejection = {
|
|
72
|
+
reason: "Correct the submitted dates.",
|
|
73
|
+
source: "Jopari",
|
|
74
|
+
receivedAt: "2026-09-01T16:11:00.000Z",
|
|
75
|
+
issues: [
|
|
76
|
+
{ code: "A6:187", description: "From Date of Service cannot be in the future" },
|
|
77
|
+
{ code: "A6:88", description: "Thru Date of Service cannot be in the future" },
|
|
78
|
+
],
|
|
79
|
+
};
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The component falls back to the legacy singular `reason` and `code` fields when `issues` is absent. Set `appearance.dangerColor` to customize its red treatment.
|
|
83
|
+
|
|
58
84
|
Procedure codes, modifiers, and rendering taxonomy all use the same styled searchable dropdown (`mindbill-combo-box`) with code + description rows, hover states, and typed custom-code entry for complete CPT/HCPCS/medical-legal codes. Service lines accept multiple modifiers rendered as removable chips. PDFs can be dropped anywhere on the screen — a full-page overlay confirms the drop target — with the same 25 MB per-file, 100 MB total, and 20-document limits as the React form.
|
|
59
85
|
|
|
60
86
|
The default reference directories and field rules live in the component library. Hosts can provide custom procedure, modifier, and taxonomy options without reimplementing the form. For a fixture or Storybook, pass an async `submitter`; omit it in production so the component talks directly to the Partner API.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { signal, computed,
|
|
2
|
+
import { signal, computed, Input, Component, EventEmitter, effect, Output, Directive, inject, ChangeDetectorRef, HostListener } from '@angular/core';
|
|
3
3
|
import { createBillLifecycleClient, createBillReferenceClient, createBillSubmissionClient, createOrganizationClient } from '@mindbill/browser';
|
|
4
4
|
import * as i1 from '@angular/common';
|
|
5
5
|
import { CommonModule } from '@angular/common';
|
|
@@ -80,11 +80,114 @@ class MindBillLifecycleStore {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
const THEMES = {
|
|
83
|
-
mindbill: { preset: "mindbill", accentColor: "#238dbd", accentTextColor: "#fff", backgroundColor: "#f3f8fa", surfaceColor: "#fff", textColor: "#203743", mutedColor: "#657982", borderColor: "#dbe6ea", borderRadius: "14px", controlRadius: "8px", fontFamily: "Inter,system-ui,sans-serif" },
|
|
84
|
-
"qme-companion": { preset: "qme-companion", accentColor: "#53b5dc", accentTextColor: "#173542", backgroundColor: "#f2f8fb", surfaceColor: "#fff", textColor: "#1d3440", mutedColor: "#617783", borderColor: "#d7e5eb", borderRadius: "12px", controlRadius: "8px", fontFamily: "Inter,system-ui,sans-serif" },
|
|
85
|
-
"orange-bright": { preset: "orange-bright", accentColor: "#f4510b", accentTextColor: "#fff", backgroundColor: "#fffaf6", surfaceColor: "#fffefd", textColor: "#090f1f", mutedColor: "#626a73", borderColor: "#e7e1da", borderRadius: "16px", controlRadius: "10px", fontFamily: "Inter,system-ui,sans-serif" },
|
|
86
|
-
"clinical-blue": { preset: "clinical-blue", accentColor: "#1677ff", accentTextColor: "#fff", backgroundColor: "#f5f7fa", surfaceColor: "#fff", textColor: "#1f2d3d", mutedColor: "#66788a", borderColor: "#d9e2ec", borderRadius: "8px", controlRadius: "6px", fontFamily: "Inter,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif" },
|
|
83
|
+
mindbill: { preset: "mindbill", accentColor: "#238dbd", accentTextColor: "#fff", backgroundColor: "#f3f8fa", surfaceColor: "#fff", textColor: "#203743", mutedColor: "#657982", borderColor: "#dbe6ea", borderRadius: "14px", controlRadius: "8px", fontFamily: "Inter,system-ui,sans-serif", dangerColor: "#b63d35" },
|
|
84
|
+
"qme-companion": { preset: "qme-companion", accentColor: "#53b5dc", accentTextColor: "#173542", backgroundColor: "#f2f8fb", surfaceColor: "#fff", textColor: "#1d3440", mutedColor: "#617783", borderColor: "#d7e5eb", borderRadius: "12px", controlRadius: "8px", fontFamily: "Inter,system-ui,sans-serif", dangerColor: "#b83f3a" },
|
|
85
|
+
"orange-bright": { preset: "orange-bright", accentColor: "#f4510b", accentTextColor: "#fff", backgroundColor: "#fffaf6", surfaceColor: "#fffefd", textColor: "#090f1f", mutedColor: "#626a73", borderColor: "#e7e1da", borderRadius: "16px", controlRadius: "10px", fontFamily: "Inter,system-ui,sans-serif", dangerColor: "#c4322b" },
|
|
86
|
+
"clinical-blue": { preset: "clinical-blue", accentColor: "#1677ff", accentTextColor: "#fff", backgroundColor: "#f5f7fa", surfaceColor: "#fff", textColor: "#1f2d3d", mutedColor: "#66788a", borderColor: "#d9e2ec", borderRadius: "8px", controlRadius: "6px", fontFamily: "Inter,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif", dangerColor: "#d4380d" },
|
|
87
87
|
};
|
|
88
|
+
function mindBillAngularAppearanceStyle(appearance = { preset: "mindbill" }) {
|
|
89
|
+
const base = THEMES[appearance.preset ?? "mindbill"];
|
|
90
|
+
return {
|
|
91
|
+
"--a": appearance.accentColor ?? base.accentColor,
|
|
92
|
+
"--ac": appearance.accentTextColor ?? base.accentTextColor,
|
|
93
|
+
"--bg": appearance.backgroundColor ?? base.backgroundColor,
|
|
94
|
+
"--s": appearance.surfaceColor ?? base.surfaceColor,
|
|
95
|
+
"--t": appearance.textColor ?? base.textColor,
|
|
96
|
+
"--m": appearance.mutedColor ?? base.mutedColor,
|
|
97
|
+
"--b": appearance.borderColor ?? base.borderColor,
|
|
98
|
+
"--r": appearance.borderRadius ?? base.borderRadius,
|
|
99
|
+
"--cr": appearance.controlRadius ?? base.controlRadius,
|
|
100
|
+
"--font": appearance.fontFamily ?? base.fontFamily,
|
|
101
|
+
"--danger": appearance.dangerColor ?? base.dangerColor,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function billRejectionIssues(rejection) {
|
|
106
|
+
return rejection.issues?.length
|
|
107
|
+
? [...rejection.issues]
|
|
108
|
+
: [{ code: rejection.code ?? null, description: rejection.reason }];
|
|
109
|
+
}
|
|
110
|
+
function billRejectionIssueSummary(rejection, count) {
|
|
111
|
+
const noun = count === 1 ? "validation error" : "validation errors";
|
|
112
|
+
const source = rejection.source?.trim().replace(/\.$/, "");
|
|
113
|
+
if (!source)
|
|
114
|
+
return `${count} ${noun}.`;
|
|
115
|
+
if (/acknowledg/i.test(source))
|
|
116
|
+
return `${count} clearinghouse ${noun}.`;
|
|
117
|
+
return `${count} ${noun} returned by ${source}.`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
class MindBillBillRejectionNoticeComponent {
|
|
121
|
+
rejection;
|
|
122
|
+
submittedAt = null;
|
|
123
|
+
title = "Rejected — action required";
|
|
124
|
+
appearance = { preset: "mindbill" };
|
|
125
|
+
get issues() { return billRejectionIssues(this.rejection); }
|
|
126
|
+
get hasCodes() { return this.issues.some((issue) => Boolean(issue.code)); }
|
|
127
|
+
get issueSummary() { return billRejectionIssueSummary(this.rejection, this.issues.length); }
|
|
128
|
+
get themeStyle() { return mindBillAngularAppearanceStyle(this.appearance); }
|
|
129
|
+
formatDate(value) { const parsed = new Date(value); return Number.isNaN(parsed.getTime()) ? value : new Intl.DateTimeFormat(undefined, { dateStyle: "medium", timeStyle: "short" }).format(parsed); }
|
|
130
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: MindBillBillRejectionNoticeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
131
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: MindBillBillRejectionNoticeComponent, isStandalone: true, selector: "mindbill-bill-rejection-notice", inputs: { rejection: "rejection", submittedAt: "submittedAt", title: "title", appearance: "appearance" }, ngImport: i0, template: `
|
|
132
|
+
<section class="notice" [ngStyle]="themeStyle" role="alert" aria-label="Bill rejection reasons">
|
|
133
|
+
<div class="overview">
|
|
134
|
+
<header>
|
|
135
|
+
<span class="icon" aria-hidden="true">!</span>
|
|
136
|
+
<div><strong>{{ title }}</strong><span>Fix the {{ issues.length === 1 ? 'issue' : 'issues' }} below, then resubmit.</span></div>
|
|
137
|
+
</header>
|
|
138
|
+
<ol class="progress" aria-label="Submission status">
|
|
139
|
+
<li><span aria-hidden="true">✓</span><b>Sent</b>@if (submittedAt) { <small>{{ formatDate(submittedAt) }}</small> }</li>
|
|
140
|
+
<li class="rejected" aria-current="step"><span aria-hidden="true">!</span><b>Rejected</b>@if (rejection.receivedAt) { <small>{{ formatDate(rejection.receivedAt) }}</small> }</li>
|
|
141
|
+
</ol>
|
|
142
|
+
</div>
|
|
143
|
+
<div class="issues" aria-label="Rejection reasons">
|
|
144
|
+
<div class="issues-heading"><strong><span aria-hidden="true">△</span> Rejection reasons</strong><span>{{ issueSummary }}</span></div>
|
|
145
|
+
<ol [attr.aria-label]="issues.length + ' rejection ' + (issues.length === 1 ? 'reason' : 'reasons')">
|
|
146
|
+
@for (issue of issues; track $index) {
|
|
147
|
+
<li><span class="bullet" aria-hidden="true"></span><p>{{ issue.description }}</p>@if (issue.code) { <span class="code">{{ issue.code }}</span> }</li>
|
|
148
|
+
}
|
|
149
|
+
</ol>
|
|
150
|
+
@if (hasCodes) { <p class="code-note"><span aria-hidden="true">i</span> Technical error codes are included for support and auditability.</p> }
|
|
151
|
+
</div>
|
|
152
|
+
</section>
|
|
153
|
+
`, isInline: true, styles: [":host{display:block}.notice,.notice *{box-sizing:border-box}.notice{font-family:var(--font,Inter,system-ui,sans-serif);color:var(--t,#203743);border:1px solid color-mix(in srgb,var(--danger) 30%,var(--b));border-left:4px solid var(--danger);border-radius:var(--r,14px);background:color-mix(in srgb,var(--danger) 5%,var(--s));box-shadow:0 14px 38px color-mix(in srgb,var(--danger) 9%,transparent);padding:26px 28px 18px}.overview{display:grid;grid-template-columns:minmax(230px,.72fr) minmax(360px,1.28fr);align-items:start;gap:38px}header{display:flex;align-items:flex-start;gap:14px}header>div{display:grid;gap:5px}header strong{color:var(--danger);font-size:19px;line-height:1.25}header div>span{color:color-mix(in srgb,var(--danger) 48%,var(--t));font-size:14px;line-height:1.45}.icon{display:grid;place-items:center;flex:0 0 auto;width:42px;height:42px;border-radius:50%;background:var(--danger);box-shadow:0 5px 14px color-mix(in srgb,var(--danger) 25%,transparent);color:#fff;font-size:21px;font-weight:900}.progress{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));list-style:none;margin:5px 0 24px;padding:0}.progress li{display:grid;grid-template-rows:34px auto auto;gap:4px;position:relative;text-align:center;font-size:12px}.progress li:first-child:after{content:\"\";position:absolute;left:calc(50% + 17px);right:calc(-50% + 17px);top:16px;height:2px;background:color-mix(in srgb,var(--danger) 48%,var(--b))}.progress li>span{display:grid;place-items:center;justify-self:center;position:relative;z-index:1;width:34px;height:34px;border:2px solid color-mix(in srgb,var(--danger) 32%,var(--b));border-radius:50%;background:var(--s);color:var(--danger);font-size:16px;font-weight:900}.progress li.rejected>span{border-color:var(--danger);background:var(--danger);color:#fff}.progress b{font-size:13px}.progress small{color:var(--m);font-size:11px;line-height:1.3}.issues{border:1px solid color-mix(in srgb,var(--danger) 19%,var(--b));border-radius:12px;background:color-mix(in srgb,var(--danger) 2%,var(--s));overflow:hidden}.issues-heading{display:flex;align-items:center;justify-content:space-between;gap:18px;padding:12px 16px;border-bottom:1px solid color-mix(in srgb,var(--danger) 14%,var(--b))}.issues-heading strong{display:flex;align-items:center;gap:8px;font-size:13px}.issues-heading strong>span{color:var(--danger);font-size:17px}.issues-heading>span{color:var(--m);font-size:12px;text-align:right}.issues ol{display:grid;gap:7px;list-style:none;margin:0;padding:10px 12px}.issues li{display:grid;grid-template-columns:8px minmax(0,1fr) auto;align-items:center;gap:11px;padding:10px 12px;border:1px solid color-mix(in srgb,var(--danger) 11%,var(--b));border-radius:8px;background:color-mix(in srgb,var(--danger) 1%,var(--s));box-shadow:0 1px 3px #1727300a}.bullet{width:7px;height:7px;border-radius:50%;background:var(--danger)}.issues p{margin:0;font-size:14px;line-height:1.45}.code{justify-self:end;border:1px solid color-mix(in srgb,var(--danger) 20%,var(--b));border-radius:999px;background:color-mix(in srgb,var(--danger) 6%,var(--s));color:var(--danger);font-size:11px;font-weight:850;line-height:1.2;padding:4px 8px}.code-note{display:flex;align-items:center;gap:7px;margin:0;padding:1px 16px 13px;color:var(--m);font-size:11px}.code-note>span{display:grid;place-items:center;width:16px;height:16px;border:1px solid currentColor;border-radius:50%;font-size:10px;font-weight:850}@media(max-width:700px){.notice{padding:18px 14px 14px}.overview{grid-template-columns:1fr;gap:18px}header strong{font-size:17px}.icon{width:36px;height:36px}.progress{margin:0 0 4px}.issues-heading{align-items:flex-start;flex-direction:column;gap:4px}.issues-heading>span{text-align:left}.issues li{grid-template-columns:8px minmax(0,1fr);gap:8px 10px}.code{grid-column:2;justify-self:start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] });
|
|
154
|
+
}
|
|
155
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: MindBillBillRejectionNoticeComponent, decorators: [{
|
|
156
|
+
type: Component,
|
|
157
|
+
args: [{ selector: "mindbill-bill-rejection-notice", standalone: true, imports: [CommonModule], template: `
|
|
158
|
+
<section class="notice" [ngStyle]="themeStyle" role="alert" aria-label="Bill rejection reasons">
|
|
159
|
+
<div class="overview">
|
|
160
|
+
<header>
|
|
161
|
+
<span class="icon" aria-hidden="true">!</span>
|
|
162
|
+
<div><strong>{{ title }}</strong><span>Fix the {{ issues.length === 1 ? 'issue' : 'issues' }} below, then resubmit.</span></div>
|
|
163
|
+
</header>
|
|
164
|
+
<ol class="progress" aria-label="Submission status">
|
|
165
|
+
<li><span aria-hidden="true">✓</span><b>Sent</b>@if (submittedAt) { <small>{{ formatDate(submittedAt) }}</small> }</li>
|
|
166
|
+
<li class="rejected" aria-current="step"><span aria-hidden="true">!</span><b>Rejected</b>@if (rejection.receivedAt) { <small>{{ formatDate(rejection.receivedAt) }}</small> }</li>
|
|
167
|
+
</ol>
|
|
168
|
+
</div>
|
|
169
|
+
<div class="issues" aria-label="Rejection reasons">
|
|
170
|
+
<div class="issues-heading"><strong><span aria-hidden="true">△</span> Rejection reasons</strong><span>{{ issueSummary }}</span></div>
|
|
171
|
+
<ol [attr.aria-label]="issues.length + ' rejection ' + (issues.length === 1 ? 'reason' : 'reasons')">
|
|
172
|
+
@for (issue of issues; track $index) {
|
|
173
|
+
<li><span class="bullet" aria-hidden="true"></span><p>{{ issue.description }}</p>@if (issue.code) { <span class="code">{{ issue.code }}</span> }</li>
|
|
174
|
+
}
|
|
175
|
+
</ol>
|
|
176
|
+
@if (hasCodes) { <p class="code-note"><span aria-hidden="true">i</span> Technical error codes are included for support and auditability.</p> }
|
|
177
|
+
</div>
|
|
178
|
+
</section>
|
|
179
|
+
`, styles: [":host{display:block}.notice,.notice *{box-sizing:border-box}.notice{font-family:var(--font,Inter,system-ui,sans-serif);color:var(--t,#203743);border:1px solid color-mix(in srgb,var(--danger) 30%,var(--b));border-left:4px solid var(--danger);border-radius:var(--r,14px);background:color-mix(in srgb,var(--danger) 5%,var(--s));box-shadow:0 14px 38px color-mix(in srgb,var(--danger) 9%,transparent);padding:26px 28px 18px}.overview{display:grid;grid-template-columns:minmax(230px,.72fr) minmax(360px,1.28fr);align-items:start;gap:38px}header{display:flex;align-items:flex-start;gap:14px}header>div{display:grid;gap:5px}header strong{color:var(--danger);font-size:19px;line-height:1.25}header div>span{color:color-mix(in srgb,var(--danger) 48%,var(--t));font-size:14px;line-height:1.45}.icon{display:grid;place-items:center;flex:0 0 auto;width:42px;height:42px;border-radius:50%;background:var(--danger);box-shadow:0 5px 14px color-mix(in srgb,var(--danger) 25%,transparent);color:#fff;font-size:21px;font-weight:900}.progress{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));list-style:none;margin:5px 0 24px;padding:0}.progress li{display:grid;grid-template-rows:34px auto auto;gap:4px;position:relative;text-align:center;font-size:12px}.progress li:first-child:after{content:\"\";position:absolute;left:calc(50% + 17px);right:calc(-50% + 17px);top:16px;height:2px;background:color-mix(in srgb,var(--danger) 48%,var(--b))}.progress li>span{display:grid;place-items:center;justify-self:center;position:relative;z-index:1;width:34px;height:34px;border:2px solid color-mix(in srgb,var(--danger) 32%,var(--b));border-radius:50%;background:var(--s);color:var(--danger);font-size:16px;font-weight:900}.progress li.rejected>span{border-color:var(--danger);background:var(--danger);color:#fff}.progress b{font-size:13px}.progress small{color:var(--m);font-size:11px;line-height:1.3}.issues{border:1px solid color-mix(in srgb,var(--danger) 19%,var(--b));border-radius:12px;background:color-mix(in srgb,var(--danger) 2%,var(--s));overflow:hidden}.issues-heading{display:flex;align-items:center;justify-content:space-between;gap:18px;padding:12px 16px;border-bottom:1px solid color-mix(in srgb,var(--danger) 14%,var(--b))}.issues-heading strong{display:flex;align-items:center;gap:8px;font-size:13px}.issues-heading strong>span{color:var(--danger);font-size:17px}.issues-heading>span{color:var(--m);font-size:12px;text-align:right}.issues ol{display:grid;gap:7px;list-style:none;margin:0;padding:10px 12px}.issues li{display:grid;grid-template-columns:8px minmax(0,1fr) auto;align-items:center;gap:11px;padding:10px 12px;border:1px solid color-mix(in srgb,var(--danger) 11%,var(--b));border-radius:8px;background:color-mix(in srgb,var(--danger) 1%,var(--s));box-shadow:0 1px 3px #1727300a}.bullet{width:7px;height:7px;border-radius:50%;background:var(--danger)}.issues p{margin:0;font-size:14px;line-height:1.45}.code{justify-self:end;border:1px solid color-mix(in srgb,var(--danger) 20%,var(--b));border-radius:999px;background:color-mix(in srgb,var(--danger) 6%,var(--s));color:var(--danger);font-size:11px;font-weight:850;line-height:1.2;padding:4px 8px}.code-note{display:flex;align-items:center;gap:7px;margin:0;padding:1px 16px 13px;color:var(--m);font-size:11px}.code-note>span{display:grid;place-items:center;width:16px;height:16px;border:1px solid currentColor;border-radius:50%;font-size:10px;font-weight:850}@media(max-width:700px){.notice{padding:18px 14px 14px}.overview{grid-template-columns:1fr;gap:18px}header strong{font-size:17px}.icon{width:36px;height:36px}.progress{margin:0 0 4px}.issues-heading{align-items:flex-start;flex-direction:column;gap:4px}.issues-heading>span{text-align:left}.issues li{grid-template-columns:8px minmax(0,1fr);gap:8px 10px}.code{grid-column:2;justify-self:start}}\n"] }]
|
|
180
|
+
}], propDecorators: { rejection: [{
|
|
181
|
+
type: Input,
|
|
182
|
+
args: [{ required: true }]
|
|
183
|
+
}], submittedAt: [{
|
|
184
|
+
type: Input
|
|
185
|
+
}], title: [{
|
|
186
|
+
type: Input
|
|
187
|
+
}], appearance: [{
|
|
188
|
+
type: Input
|
|
189
|
+
}] } });
|
|
190
|
+
|
|
88
191
|
class MindBillBillLifecycleComponent {
|
|
89
192
|
billId = "";
|
|
90
193
|
sessionEndpoint = "/api/mindbill/session";
|
|
@@ -117,8 +220,7 @@ class MindBillBillLifecycleComponent {
|
|
|
117
220
|
}
|
|
118
221
|
ngOnDestroy() { this.store.disconnect(); }
|
|
119
222
|
get themeStyle() {
|
|
120
|
-
|
|
121
|
-
return { "--a": this.appearance.accentColor ?? base.accentColor, "--ac": this.appearance.accentTextColor ?? base.accentTextColor, "--bg": this.appearance.backgroundColor ?? base.backgroundColor, "--s": this.appearance.surfaceColor ?? base.surfaceColor, "--t": this.appearance.textColor ?? base.textColor, "--m": this.appearance.mutedColor ?? base.mutedColor, "--b": this.appearance.borderColor ?? base.borderColor, "--r": this.appearance.borderRadius ?? base.borderRadius, "--cr": this.appearance.controlRadius ?? base.controlRadius, "--font": this.appearance.fontFamily ?? base.fontFamily };
|
|
223
|
+
return mindBillAngularAppearanceStyle(this.appearance);
|
|
122
224
|
}
|
|
123
225
|
stateLabel(data) { return data.lifecycle.state.replace(/_/g, " ").replace(/\b\w/g, (value) => value.toUpperCase()); }
|
|
124
226
|
lifecycleDetail(data) { return data.lifecycle.submittedAt ? `Submitted ${new Date(data.lifecycle.submittedAt).toLocaleDateString()}${data.lifecycle.agingDays != null ? ` · ${data.lifecycle.agingDays} days old` : ""}` : "Submitted bill"; }
|
|
@@ -128,6 +230,13 @@ class MindBillBillLifecycleComponent {
|
|
|
128
230
|
return 2; if (value.includes("accepted"))
|
|
129
231
|
return 1; return 0; }
|
|
130
232
|
activityLabel(type) { return type.replace(/[._-]+/g, " ").replace(/\b\w/g, (value) => value.toUpperCase()); }
|
|
233
|
+
historyExpanded = new Set();
|
|
234
|
+
toggleHistory(id) { if (this.historyExpanded.has(id))
|
|
235
|
+
this.historyExpanded.delete(id);
|
|
236
|
+
else
|
|
237
|
+
this.historyExpanded.add(id); }
|
|
238
|
+
isHistoryOpen(id) { return this.historyExpanded.has(id); }
|
|
239
|
+
historyDate(iso) { const parsed = new Date(iso); return Number.isNaN(parsed.getTime()) ? iso : parsed.toLocaleDateString("en-US", { month: "2-digit", day: "2-digit", year: "numeric" }); }
|
|
131
240
|
payerContact(data) { const contacts = data.delivery.contacts; return contacts.adjusterName || contacts.adjusterPhone || contacts.adjusterEmail || contacts.claimsEmail || contacts.faxNumber || contacts.mailingAddress || "No payer contact details are available."; }
|
|
132
241
|
beginAction(action, data) { if (action === "post_payment")
|
|
133
242
|
this.panel = "payment";
|
|
@@ -159,9 +268,18 @@ class MindBillBillLifecycleComponent {
|
|
|
159
268
|
<div class="money"><span>{{ data.bill.balanceDue | currency }}</span><small>balance</small></div>
|
|
160
269
|
</header>
|
|
161
270
|
|
|
162
|
-
|
|
163
|
-
<
|
|
164
|
-
|
|
271
|
+
@if (data.lifecycle.state.toLowerCase() === 'rejected' && data.rejection) {
|
|
272
|
+
<mindbill-bill-rejection-notice
|
|
273
|
+
class="rejection"
|
|
274
|
+
[rejection]="data.rejection"
|
|
275
|
+
[submittedAt]="data.lifecycle.submittedAt ?? null"
|
|
276
|
+
[appearance]="appearance"
|
|
277
|
+
/>
|
|
278
|
+
} @else {
|
|
279
|
+
<section class="card progress" aria-label="Bill lifecycle">
|
|
280
|
+
<ol>@for (stage of lifecycleStages; track stage.id; let i=$index) { <li [class.complete]="i < currentStageIndex(data)" [class.current]="i === currentStageIndex(data)"><b>{{ i < currentStageIndex(data) ? '✓' : i + 1 }}</b><span>{{ stage.label }}</span></li> }</ol>
|
|
281
|
+
</section>
|
|
282
|
+
}
|
|
165
283
|
|
|
166
284
|
<section class="card snapshot" aria-label="Bill snapshot">
|
|
167
285
|
<div class="card-title"><div><h3>Bill snapshot</h3><p>The immutable values submitted to the payer.</p></div></div>
|
|
@@ -177,7 +295,42 @@ class MindBillBillLifecycleComponent {
|
|
|
177
295
|
|
|
178
296
|
<section class="card"><div class="card-title"><div><h3>Payments</h3><p>Payments posted to this bill.</p></div><span>{{ data.payments.length }}</span></div>@if (data.payments.length) { <ul class="rows">@for (payment of data.payments; track payment.id) { <li><div><strong>{{ payment.amount | currency }}</strong><small>{{ payment.method | uppercase }} · {{ payment.depositDate || payment.postedAt }}</small></div></li> }</ul> } @else { <p class="muted">No payments posted.</p> }</section>
|
|
179
297
|
|
|
180
|
-
<section class="card"><div class="card-title"><div><h3>Bill history</h3><p>
|
|
298
|
+
<section class="card"><div class="card-title"><div><h3>Bill history</h3><p>Submissions, payer responses, follow-up, and payments.</p></div><span>{{ data.history?.length ?? data.activity.length }}</span></div>
|
|
299
|
+
@if (data.history?.length) {
|
|
300
|
+
<!-- daisyBill-style Date/Action/User/Details table: submissions are pale-blue
|
|
301
|
+
expandable rows (documents + compliance due dates), notes pale-yellow,
|
|
302
|
+
277 responses expand into decoded status-code sentences. Rows arrive
|
|
303
|
+
pre-worded from the MindBill API (BillLifecycleData.history). -->
|
|
304
|
+
<div class="history">
|
|
305
|
+
<div class="history-head" aria-hidden="true"><span>Date</span><span>Action</span><span>User</span><span>Details</span></div>
|
|
306
|
+
<ul class="history-rows">
|
|
307
|
+
@for (entry of data.history; track entry.id) {
|
|
308
|
+
<li [class.is-submission]="entry.tone === 'submission'" [class.is-note]="entry.tone === 'note'" [class.is-problem]="entry.tone === 'problem'">
|
|
309
|
+
<button type="button" class="history-line" [disabled]="!entry.details" [attr.aria-expanded]="entry.details ? isHistoryOpen(entry.id) : null" (click)="toggleHistory(entry.id)">
|
|
310
|
+
<span class="h-date">{{ historyDate(entry.date) }}</span>
|
|
311
|
+
<span class="h-action">{{ entry.action }}</span>
|
|
312
|
+
<span class="h-actor">{{ entry.actor || '—' }}</span>
|
|
313
|
+
<span class="h-summary">@if (entry.details) { <i [class.open]="isHistoryOpen(entry.id)" aria-hidden="true"></i> }{{ entry.summary }}</span>
|
|
314
|
+
</button>
|
|
315
|
+
@if (entry.details; as details) {
|
|
316
|
+
@if (isHistoryOpen(entry.id)) {
|
|
317
|
+
<div class="history-details">
|
|
318
|
+
@if (details.rows?.length) { <dl class="h-kv">@for (row of details.rows ?? []; track row.label) { <div><dt>{{ row.label }}</dt><dd>{{ row.value }}</dd></div> }</dl> }
|
|
319
|
+
@if (details.documents?.length) { <dl class="h-kv"><div><dt>Documents</dt><dd>@for (doc of details.documents ?? []; track doc.id) { <span class="h-doc">{{ doc.filename }}</span> }</dd></div></dl> }
|
|
320
|
+
@if (details.codes?.length) { <dl class="h-codes">@for (code of details.codes ?? []; track code.code) { <div><dt>{{ code.code }}</dt><dd>{{ code.text }}</dd></div> }</dl> }
|
|
321
|
+
@if (details.complianceDueDates?.length) { <div class="h-due"><em>Compliance Due Dates</em><dl>@for (due of details.complianceDueDates ?? []; track due.text) { <div><dt>{{ historyDate(due.date) }}</dt><dd>{{ due.text }}</dd></div> }</dl></div> }
|
|
322
|
+
@if (details.text) { <p class="h-text">{{ details.text }}</p> }
|
|
323
|
+
</div>
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
</li>
|
|
327
|
+
}
|
|
328
|
+
</ul>
|
|
329
|
+
</div>
|
|
330
|
+
} @else {
|
|
331
|
+
<ul class="timeline">@for (event of data.activity; track event.id) { <li><i></i><div><strong>{{ activityLabel(event.type) }}</strong><p>{{ event.description }}</p><small>{{ event.createdAt | date:'medium' }}</small></div></li> }</ul>
|
|
332
|
+
}
|
|
333
|
+
</section>
|
|
181
334
|
|
|
182
335
|
@if (notice) { <p class="notice">{{ notice }}</p> }
|
|
183
336
|
<div class="actions">@for (action of data.lifecycle.actions; track action.id) { @if (action.id !== 'view_eor' || data.eors.length) { <button type="button" [class.primary]="action.primary" [disabled]="!action.enabled || store.mutating()" (click)="beginAction(action.id, data)">{{ action.label }}</button> } }</div>
|
|
@@ -187,11 +340,11 @@ class MindBillBillLifecycleComponent {
|
|
|
187
340
|
@if (panel === 'close') { <form class="card action-form" (submit)="$event.preventDefault(); closeBill()"><h3>Close bill</h3><label>Reason<textarea required [(ngModel)]="closeReason" name="closeReason"></textarea></label><div><button type="button" (click)="panel=''">Cancel</button><button class="primary" type="submit">Close bill</button></div></form> }
|
|
188
341
|
}
|
|
189
342
|
</section>
|
|
190
|
-
`, isInline: true, styles: [":host{display:block}.mb{font-family:var(--font);color:var(--t);background:var(--bg);padding:20px}.summary,.card,.columns{max-width:1120px;margin:0 auto 16px}.summary{display:flex;justify-content:space-between;align-items:end}.summary h2{margin:4px 0}.summary p,.muted{color:var(--m)}.eyebrow,dt{font-size:12px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--m)}.money{text-align:right}.money span{display:block;font-size:28px;font-weight:800}.money small{color:var(--m)}.card{background:var(--s);border:1px solid var(--b);border-radius:var(--r);padding:20px}.card-title{display:flex;justify-content:space-between;gap:20px}.card-title h3{margin:0}.card-title p{margin:5px 0;color:var(--m)}.progress ol{display:flex;list-style:none;padding:0;margin:0}.progress li{flex:1;text-align:center;position:relative;color:var(--m)}.progress li:before{content:\"\";position:absolute;top:16px;left:0;right:0;border-top:2px solid var(--b)}.progress li:first-child:before{left:50%}.progress li:last-child:before{right:50%}.progress b{position:relative;z-index:1;display:grid;place-items:center;width:32px;height:32px;margin:auto;border:2px solid var(--b);border-radius:50%;background:var(--s)}.progress .complete b,.progress .current b{border-color:var(--a);background:var(--a);color:var(--ac)}.progress span{display:block;margin-top:8px;font-size:12px;font-weight:700}.snapshot dl,.columns dl{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}.snapshot dd,.columns dd{font-weight:700;margin:6px 0}.columns{display:grid;grid-template-columns:1fr 1fr;gap:16px}.columns .card{margin:0}.rows,.timeline{list-style:none;margin:12px 0 0;padding:0}.rows li{display:flex;justify-content:space-between;align-items:center;padding:12px 0;border-top:1px solid var(--b)}.rows small{display:block;color:var(--m);margin-top:3px}.timeline li{display:flex;gap:14px;padding:10px 0}.timeline i{width:10px;height:10px;margin-top:5px;border:3px solid var(--a);border-radius:50%}.timeline p{margin:4px 0}.timeline small{color:var(--m)}button,input,select,textarea{font:inherit;border:1px solid var(--b);border-radius:var(--cr);padding:9px 12px;background:var(--s);color:var(--t)}button{cursor:pointer;font-weight:700}.primary{background:var(--a);border-color:var(--a);color:var(--ac)}.actions{max-width:1120px;margin:0 auto;display:flex;justify-content:flex-end;gap:10px}.action-form{display:grid;gap:12px}.action-form label{display:grid;gap:5px;font-weight:700}.action-form div{display:flex;justify-content:flex-end;gap:10px}.state{max-width:720px;margin:auto;padding:32px;text-align:center}.state span{display:block;margin:8px}.error{color:#9b1c1c}.notice{max-width:1120px;margin:0 auto 12px;color:var(--m)}@media(max-width:760px){.mb{padding:12px}.summary,.columns{display:block}.money{text-align:left;margin-top:12px}.columns .card{margin-bottom:12px}.snapshot dl,.columns dl{grid-template-columns:1fr 1fr}.progress span{font-size:10px}.card{padding:15px}}\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.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" }] });
|
|
343
|
+
`, isInline: true, styles: [":host{display:block}.mb{font-family:var(--font);color:var(--t);background:var(--bg);padding:20px}.summary,.card,.columns,.rejection{display:block;max-width:1120px;margin:0 auto 16px}.summary{display:flex;justify-content:space-between;align-items:end}.summary h2{margin:4px 0}.summary p,.muted{color:var(--m)}.eyebrow,dt{font-size:12px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--m)}.money{text-align:right}.money span{display:block;font-size:28px;font-weight:800}.money small{color:var(--m)}.card{background:var(--s);border:1px solid var(--b);border-radius:var(--r);padding:20px}.card-title{display:flex;justify-content:space-between;gap:20px}.card-title h3{margin:0}.card-title p{margin:5px 0;color:var(--m)}.progress ol{display:flex;list-style:none;padding:0;margin:0}.progress li{flex:1;text-align:center;position:relative;color:var(--m)}.progress li:before{content:\"\";position:absolute;top:16px;left:0;right:0;border-top:2px solid var(--b)}.progress li:first-child:before{left:50%}.progress li:last-child:before{right:50%}.progress b{position:relative;z-index:1;display:grid;place-items:center;width:32px;height:32px;margin:auto;border:2px solid var(--b);border-radius:50%;background:var(--s)}.progress .complete b,.progress .current b{border-color:var(--a);background:var(--a);color:var(--ac)}.progress span{display:block;margin-top:8px;font-size:12px;font-weight:700}.snapshot dl,.columns dl{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}.snapshot dd,.columns dd{font-weight:700;margin:6px 0}.columns{display:grid;grid-template-columns:1fr 1fr;gap:16px}.columns .card{margin:0}.rows,.timeline{list-style:none;margin:12px 0 0;padding:0}.rows li{display:flex;justify-content:space-between;align-items:center;padding:12px 0;border-top:1px solid var(--b)}.rows small{display:block;color:var(--m);margin-top:3px}.timeline li{display:flex;gap:14px;padding:10px 0}.timeline i{width:10px;height:10px;margin-top:5px;border:3px solid var(--a);border-radius:50%}.timeline p{margin:4px 0}.timeline small{color:var(--m)}button,input,select,textarea{font:inherit;border:1px solid var(--b);border-radius:var(--cr);padding:9px 12px;background:var(--s);color:var(--t)}button{cursor:pointer;font-weight:700}.primary{background:var(--a);border-color:var(--a);color:var(--ac)}.actions{max-width:1120px;margin:0 auto;display:flex;justify-content:flex-end;gap:10px}.action-form{display:grid;gap:12px}.action-form label{display:grid;gap:5px;font-weight:700}.action-form div{display:flex;justify-content:flex-end;gap:10px}.state{max-width:720px;margin:auto;padding:32px;text-align:center}.state span{display:block;margin:8px}.error{color:#9b1c1c}.notice{max-width:1120px;margin:0 auto 12px;color:var(--m)}@media(max-width:760px){.mb{padding:12px}.summary,.columns{display:block}.money{text-align:left;margin-top:12px}.columns .card{margin-bottom:12px}.snapshot dl,.columns dl{grid-template-columns:1fr 1fr}.progress span{font-size:10px}.card{padding:15px}}.history{border:1px solid var(--b);border-radius:10px;overflow:hidden;margin-top:12px}.history-head{display:grid;grid-template-columns:96px 150px 130px minmax(0,1fr);gap:12px;padding:8px 14px;background:color-mix(in srgb,var(--b) 26%,var(--s));color:var(--m);font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.history-rows{list-style:none;margin:0;padding:0}.history-rows>li{border-top:1px solid var(--b)}.history-rows>li.is-submission{background:color-mix(in srgb,var(--a) 9%,var(--s))}.history-rows>li.is-note{background:color-mix(in srgb,#f2c344 13%,var(--s))}.history-line{display:grid;grid-template-columns:96px 150px 130px minmax(0,1fr);gap:12px;width:100%;padding:10px 14px;border:0;border-radius:0;background:transparent;text-align:left;font-weight:400;cursor:default}.history-line:not(:disabled){cursor:pointer}.history-line:not(:disabled):hover .h-summary{text-decoration:underline}.h-date,.h-actor{color:var(--m);font-size:13px;overflow-wrap:anywhere}.h-action{font-weight:700;font-size:13.5px}.is-problem .h-action{color:#9b1c1c}.h-summary{display:flex;gap:7px;min-width:0;font-size:13.5px;line-height:1.45;overflow-wrap:anywhere}.h-summary i{flex:0 0 auto;margin-top:5px;width:0;height:0;border-top:4px solid transparent;border-bottom:4px solid transparent;border-left:6px solid var(--m);border-radius:0;transition:transform .12s ease}.h-summary i.open{transform:rotate(90deg)}.history-details{padding:4px 14px 14px 122px;font-size:13.5px;line-height:1.5}.h-kv,.h-codes,.h-due dl{display:grid;gap:4px;margin:0}.h-kv>div{display:grid;grid-template-columns:180px minmax(0,1fr);gap:12px;border-top:1px solid color-mix(in srgb,var(--b) 60%,transparent);padding-top:4px}.h-kv dt,.h-codes dt,.h-due dt{font-size:13px;font-weight:700;letter-spacing:0;text-transform:none;color:var(--t)}.h-kv dd,.h-codes dd,.h-due dd{margin:0;overflow-wrap:anywhere}.h-doc{display:block}.h-codes>div{display:grid;grid-template-columns:56px minmax(0,1fr);gap:12px}.h-codes dt{font-weight:800}.h-due{margin-top:8px}.h-due em{display:block;color:var(--m);font-size:12.5px;margin-bottom:4px}.h-due dl>div{display:grid;grid-template-columns:96px minmax(0,1fr);gap:12px;border-top:1px solid color-mix(in srgb,var(--b) 60%,transparent);padding-top:4px}.h-text{margin:8px 0 0;white-space:pre-wrap}@media(max-width:760px){.history-head{display:none}.history-line{grid-template-columns:1fr;gap:2px;padding:10px 12px}.history-details{padding:4px 12px 12px}.h-kv>div,.h-due dl>div{grid-template-columns:1fr}}\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.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: "component", type: MindBillBillRejectionNoticeComponent, selector: "mindbill-bill-rejection-notice", inputs: ["rejection", "submittedAt", "title", "appearance"] }, { kind: "pipe", type: i1.UpperCasePipe, name: "uppercase" }, { kind: "pipe", type: i1.CurrencyPipe, name: "currency" }, { kind: "pipe", type: i1.DatePipe, name: "date" }] });
|
|
191
344
|
}
|
|
192
345
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: MindBillBillLifecycleComponent, decorators: [{
|
|
193
346
|
type: Component,
|
|
194
|
-
args: [{ selector: "mindbill-bill-lifecycle", standalone: true, imports: [CommonModule, FormsModule], template: `
|
|
347
|
+
args: [{ selector: "mindbill-bill-lifecycle", standalone: true, imports: [CommonModule, FormsModule, MindBillBillRejectionNoticeComponent], template: `
|
|
195
348
|
<section class="mb" [ngStyle]="themeStyle">
|
|
196
349
|
@if (!billId) {
|
|
197
350
|
<div class="state error"><strong>A submitted bill ID is required.</strong></div>
|
|
@@ -205,9 +358,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
205
358
|
<div class="money"><span>{{ data.bill.balanceDue | currency }}</span><small>balance</small></div>
|
|
206
359
|
</header>
|
|
207
360
|
|
|
208
|
-
|
|
209
|
-
<
|
|
210
|
-
|
|
361
|
+
@if (data.lifecycle.state.toLowerCase() === 'rejected' && data.rejection) {
|
|
362
|
+
<mindbill-bill-rejection-notice
|
|
363
|
+
class="rejection"
|
|
364
|
+
[rejection]="data.rejection"
|
|
365
|
+
[submittedAt]="data.lifecycle.submittedAt ?? null"
|
|
366
|
+
[appearance]="appearance"
|
|
367
|
+
/>
|
|
368
|
+
} @else {
|
|
369
|
+
<section class="card progress" aria-label="Bill lifecycle">
|
|
370
|
+
<ol>@for (stage of lifecycleStages; track stage.id; let i=$index) { <li [class.complete]="i < currentStageIndex(data)" [class.current]="i === currentStageIndex(data)"><b>{{ i < currentStageIndex(data) ? '✓' : i + 1 }}</b><span>{{ stage.label }}</span></li> }</ol>
|
|
371
|
+
</section>
|
|
372
|
+
}
|
|
211
373
|
|
|
212
374
|
<section class="card snapshot" aria-label="Bill snapshot">
|
|
213
375
|
<div class="card-title"><div><h3>Bill snapshot</h3><p>The immutable values submitted to the payer.</p></div></div>
|
|
@@ -223,7 +385,42 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
223
385
|
|
|
224
386
|
<section class="card"><div class="card-title"><div><h3>Payments</h3><p>Payments posted to this bill.</p></div><span>{{ data.payments.length }}</span></div>@if (data.payments.length) { <ul class="rows">@for (payment of data.payments; track payment.id) { <li><div><strong>{{ payment.amount | currency }}</strong><small>{{ payment.method | uppercase }} · {{ payment.depositDate || payment.postedAt }}</small></div></li> }</ul> } @else { <p class="muted">No payments posted.</p> }</section>
|
|
225
387
|
|
|
226
|
-
<section class="card"><div class="card-title"><div><h3>Bill history</h3><p>
|
|
388
|
+
<section class="card"><div class="card-title"><div><h3>Bill history</h3><p>Submissions, payer responses, follow-up, and payments.</p></div><span>{{ data.history?.length ?? data.activity.length }}</span></div>
|
|
389
|
+
@if (data.history?.length) {
|
|
390
|
+
<!-- daisyBill-style Date/Action/User/Details table: submissions are pale-blue
|
|
391
|
+
expandable rows (documents + compliance due dates), notes pale-yellow,
|
|
392
|
+
277 responses expand into decoded status-code sentences. Rows arrive
|
|
393
|
+
pre-worded from the MindBill API (BillLifecycleData.history). -->
|
|
394
|
+
<div class="history">
|
|
395
|
+
<div class="history-head" aria-hidden="true"><span>Date</span><span>Action</span><span>User</span><span>Details</span></div>
|
|
396
|
+
<ul class="history-rows">
|
|
397
|
+
@for (entry of data.history; track entry.id) {
|
|
398
|
+
<li [class.is-submission]="entry.tone === 'submission'" [class.is-note]="entry.tone === 'note'" [class.is-problem]="entry.tone === 'problem'">
|
|
399
|
+
<button type="button" class="history-line" [disabled]="!entry.details" [attr.aria-expanded]="entry.details ? isHistoryOpen(entry.id) : null" (click)="toggleHistory(entry.id)">
|
|
400
|
+
<span class="h-date">{{ historyDate(entry.date) }}</span>
|
|
401
|
+
<span class="h-action">{{ entry.action }}</span>
|
|
402
|
+
<span class="h-actor">{{ entry.actor || '—' }}</span>
|
|
403
|
+
<span class="h-summary">@if (entry.details) { <i [class.open]="isHistoryOpen(entry.id)" aria-hidden="true"></i> }{{ entry.summary }}</span>
|
|
404
|
+
</button>
|
|
405
|
+
@if (entry.details; as details) {
|
|
406
|
+
@if (isHistoryOpen(entry.id)) {
|
|
407
|
+
<div class="history-details">
|
|
408
|
+
@if (details.rows?.length) { <dl class="h-kv">@for (row of details.rows ?? []; track row.label) { <div><dt>{{ row.label }}</dt><dd>{{ row.value }}</dd></div> }</dl> }
|
|
409
|
+
@if (details.documents?.length) { <dl class="h-kv"><div><dt>Documents</dt><dd>@for (doc of details.documents ?? []; track doc.id) { <span class="h-doc">{{ doc.filename }}</span> }</dd></div></dl> }
|
|
410
|
+
@if (details.codes?.length) { <dl class="h-codes">@for (code of details.codes ?? []; track code.code) { <div><dt>{{ code.code }}</dt><dd>{{ code.text }}</dd></div> }</dl> }
|
|
411
|
+
@if (details.complianceDueDates?.length) { <div class="h-due"><em>Compliance Due Dates</em><dl>@for (due of details.complianceDueDates ?? []; track due.text) { <div><dt>{{ historyDate(due.date) }}</dt><dd>{{ due.text }}</dd></div> }</dl></div> }
|
|
412
|
+
@if (details.text) { <p class="h-text">{{ details.text }}</p> }
|
|
413
|
+
</div>
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
</li>
|
|
417
|
+
}
|
|
418
|
+
</ul>
|
|
419
|
+
</div>
|
|
420
|
+
} @else {
|
|
421
|
+
<ul class="timeline">@for (event of data.activity; track event.id) { <li><i></i><div><strong>{{ activityLabel(event.type) }}</strong><p>{{ event.description }}</p><small>{{ event.createdAt | date:'medium' }}</small></div></li> }</ul>
|
|
422
|
+
}
|
|
423
|
+
</section>
|
|
227
424
|
|
|
228
425
|
@if (notice) { <p class="notice">{{ notice }}</p> }
|
|
229
426
|
<div class="actions">@for (action of data.lifecycle.actions; track action.id) { @if (action.id !== 'view_eor' || data.eors.length) { <button type="button" [class.primary]="action.primary" [disabled]="!action.enabled || store.mutating()" (click)="beginAction(action.id, data)">{{ action.label }}</button> } }</div>
|
|
@@ -233,7 +430,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
233
430
|
@if (panel === 'close') { <form class="card action-form" (submit)="$event.preventDefault(); closeBill()"><h3>Close bill</h3><label>Reason<textarea required [(ngModel)]="closeReason" name="closeReason"></textarea></label><div><button type="button" (click)="panel=''">Cancel</button><button class="primary" type="submit">Close bill</button></div></form> }
|
|
234
431
|
}
|
|
235
432
|
</section>
|
|
236
|
-
`, styles: [":host{display:block}.mb{font-family:var(--font);color:var(--t);background:var(--bg);padding:20px}.summary,.card,.columns{max-width:1120px;margin:0 auto 16px}.summary{display:flex;justify-content:space-between;align-items:end}.summary h2{margin:4px 0}.summary p,.muted{color:var(--m)}.eyebrow,dt{font-size:12px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--m)}.money{text-align:right}.money span{display:block;font-size:28px;font-weight:800}.money small{color:var(--m)}.card{background:var(--s);border:1px solid var(--b);border-radius:var(--r);padding:20px}.card-title{display:flex;justify-content:space-between;gap:20px}.card-title h3{margin:0}.card-title p{margin:5px 0;color:var(--m)}.progress ol{display:flex;list-style:none;padding:0;margin:0}.progress li{flex:1;text-align:center;position:relative;color:var(--m)}.progress li:before{content:\"\";position:absolute;top:16px;left:0;right:0;border-top:2px solid var(--b)}.progress li:first-child:before{left:50%}.progress li:last-child:before{right:50%}.progress b{position:relative;z-index:1;display:grid;place-items:center;width:32px;height:32px;margin:auto;border:2px solid var(--b);border-radius:50%;background:var(--s)}.progress .complete b,.progress .current b{border-color:var(--a);background:var(--a);color:var(--ac)}.progress span{display:block;margin-top:8px;font-size:12px;font-weight:700}.snapshot dl,.columns dl{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}.snapshot dd,.columns dd{font-weight:700;margin:6px 0}.columns{display:grid;grid-template-columns:1fr 1fr;gap:16px}.columns .card{margin:0}.rows,.timeline{list-style:none;margin:12px 0 0;padding:0}.rows li{display:flex;justify-content:space-between;align-items:center;padding:12px 0;border-top:1px solid var(--b)}.rows small{display:block;color:var(--m);margin-top:3px}.timeline li{display:flex;gap:14px;padding:10px 0}.timeline i{width:10px;height:10px;margin-top:5px;border:3px solid var(--a);border-radius:50%}.timeline p{margin:4px 0}.timeline small{color:var(--m)}button,input,select,textarea{font:inherit;border:1px solid var(--b);border-radius:var(--cr);padding:9px 12px;background:var(--s);color:var(--t)}button{cursor:pointer;font-weight:700}.primary{background:var(--a);border-color:var(--a);color:var(--ac)}.actions{max-width:1120px;margin:0 auto;display:flex;justify-content:flex-end;gap:10px}.action-form{display:grid;gap:12px}.action-form label{display:grid;gap:5px;font-weight:700}.action-form div{display:flex;justify-content:flex-end;gap:10px}.state{max-width:720px;margin:auto;padding:32px;text-align:center}.state span{display:block;margin:8px}.error{color:#9b1c1c}.notice{max-width:1120px;margin:0 auto 12px;color:var(--m)}@media(max-width:760px){.mb{padding:12px}.summary,.columns{display:block}.money{text-align:left;margin-top:12px}.columns .card{margin-bottom:12px}.snapshot dl,.columns dl{grid-template-columns:1fr 1fr}.progress span{font-size:10px}.card{padding:15px}}\n"] }]
|
|
433
|
+
`, styles: [":host{display:block}.mb{font-family:var(--font);color:var(--t);background:var(--bg);padding:20px}.summary,.card,.columns,.rejection{display:block;max-width:1120px;margin:0 auto 16px}.summary{display:flex;justify-content:space-between;align-items:end}.summary h2{margin:4px 0}.summary p,.muted{color:var(--m)}.eyebrow,dt{font-size:12px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--m)}.money{text-align:right}.money span{display:block;font-size:28px;font-weight:800}.money small{color:var(--m)}.card{background:var(--s);border:1px solid var(--b);border-radius:var(--r);padding:20px}.card-title{display:flex;justify-content:space-between;gap:20px}.card-title h3{margin:0}.card-title p{margin:5px 0;color:var(--m)}.progress ol{display:flex;list-style:none;padding:0;margin:0}.progress li{flex:1;text-align:center;position:relative;color:var(--m)}.progress li:before{content:\"\";position:absolute;top:16px;left:0;right:0;border-top:2px solid var(--b)}.progress li:first-child:before{left:50%}.progress li:last-child:before{right:50%}.progress b{position:relative;z-index:1;display:grid;place-items:center;width:32px;height:32px;margin:auto;border:2px solid var(--b);border-radius:50%;background:var(--s)}.progress .complete b,.progress .current b{border-color:var(--a);background:var(--a);color:var(--ac)}.progress span{display:block;margin-top:8px;font-size:12px;font-weight:700}.snapshot dl,.columns dl{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}.snapshot dd,.columns dd{font-weight:700;margin:6px 0}.columns{display:grid;grid-template-columns:1fr 1fr;gap:16px}.columns .card{margin:0}.rows,.timeline{list-style:none;margin:12px 0 0;padding:0}.rows li{display:flex;justify-content:space-between;align-items:center;padding:12px 0;border-top:1px solid var(--b)}.rows small{display:block;color:var(--m);margin-top:3px}.timeline li{display:flex;gap:14px;padding:10px 0}.timeline i{width:10px;height:10px;margin-top:5px;border:3px solid var(--a);border-radius:50%}.timeline p{margin:4px 0}.timeline small{color:var(--m)}button,input,select,textarea{font:inherit;border:1px solid var(--b);border-radius:var(--cr);padding:9px 12px;background:var(--s);color:var(--t)}button{cursor:pointer;font-weight:700}.primary{background:var(--a);border-color:var(--a);color:var(--ac)}.actions{max-width:1120px;margin:0 auto;display:flex;justify-content:flex-end;gap:10px}.action-form{display:grid;gap:12px}.action-form label{display:grid;gap:5px;font-weight:700}.action-form div{display:flex;justify-content:flex-end;gap:10px}.state{max-width:720px;margin:auto;padding:32px;text-align:center}.state span{display:block;margin:8px}.error{color:#9b1c1c}.notice{max-width:1120px;margin:0 auto 12px;color:var(--m)}@media(max-width:760px){.mb{padding:12px}.summary,.columns{display:block}.money{text-align:left;margin-top:12px}.columns .card{margin-bottom:12px}.snapshot dl,.columns dl{grid-template-columns:1fr 1fr}.progress span{font-size:10px}.card{padding:15px}}.history{border:1px solid var(--b);border-radius:10px;overflow:hidden;margin-top:12px}.history-head{display:grid;grid-template-columns:96px 150px 130px minmax(0,1fr);gap:12px;padding:8px 14px;background:color-mix(in srgb,var(--b) 26%,var(--s));color:var(--m);font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.history-rows{list-style:none;margin:0;padding:0}.history-rows>li{border-top:1px solid var(--b)}.history-rows>li.is-submission{background:color-mix(in srgb,var(--a) 9%,var(--s))}.history-rows>li.is-note{background:color-mix(in srgb,#f2c344 13%,var(--s))}.history-line{display:grid;grid-template-columns:96px 150px 130px minmax(0,1fr);gap:12px;width:100%;padding:10px 14px;border:0;border-radius:0;background:transparent;text-align:left;font-weight:400;cursor:default}.history-line:not(:disabled){cursor:pointer}.history-line:not(:disabled):hover .h-summary{text-decoration:underline}.h-date,.h-actor{color:var(--m);font-size:13px;overflow-wrap:anywhere}.h-action{font-weight:700;font-size:13.5px}.is-problem .h-action{color:#9b1c1c}.h-summary{display:flex;gap:7px;min-width:0;font-size:13.5px;line-height:1.45;overflow-wrap:anywhere}.h-summary i{flex:0 0 auto;margin-top:5px;width:0;height:0;border-top:4px solid transparent;border-bottom:4px solid transparent;border-left:6px solid var(--m);border-radius:0;transition:transform .12s ease}.h-summary i.open{transform:rotate(90deg)}.history-details{padding:4px 14px 14px 122px;font-size:13.5px;line-height:1.5}.h-kv,.h-codes,.h-due dl{display:grid;gap:4px;margin:0}.h-kv>div{display:grid;grid-template-columns:180px minmax(0,1fr);gap:12px;border-top:1px solid color-mix(in srgb,var(--b) 60%,transparent);padding-top:4px}.h-kv dt,.h-codes dt,.h-due dt{font-size:13px;font-weight:700;letter-spacing:0;text-transform:none;color:var(--t)}.h-kv dd,.h-codes dd,.h-due dd{margin:0;overflow-wrap:anywhere}.h-doc{display:block}.h-codes>div{display:grid;grid-template-columns:56px minmax(0,1fr);gap:12px}.h-codes dt{font-weight:800}.h-due{margin-top:8px}.h-due em{display:block;color:var(--m);font-size:12.5px;margin-bottom:4px}.h-due dl>div{display:grid;grid-template-columns:96px minmax(0,1fr);gap:12px;border-top:1px solid color-mix(in srgb,var(--b) 60%,transparent);padding-top:4px}.h-text{margin:8px 0 0;white-space:pre-wrap}@media(max-width:760px){.history-head{display:none}.history-line{grid-template-columns:1fr;gap:2px;padding:10px 12px}.history-details{padding:4px 12px 12px}.h-kv>div,.h-due dl>div{grid-template-columns:1fr}}\n"] }]
|
|
237
434
|
}], ctorParameters: () => [], propDecorators: { billId: [{
|
|
238
435
|
type: Input
|
|
239
436
|
}], sessionEndpoint: [{
|
|
@@ -2037,5 +2234,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
2037
2234
|
* Generated bundle index. Do not edit.
|
|
2038
2235
|
*/
|
|
2039
2236
|
|
|
2040
|
-
export { MINDBILL_STATUS_AGING_BUCKETS, MindBillBillAgingSummaryComponent, MindBillBillLifecycleComponent, MindBillBillListComponent, MindBillBillSubmissionComponent, MindBillBillingDashboardComponent, MindBillBillingManagementButtonComponent, MindBillBillingReportComponent, MindBillComboBoxComponent, MindBillDateInputComponent, MindBillLifecycleStore, MindBillOrganizationOnboardingComponent, MindBillStatusAgingMatrixComponent, buildMindBillReportCsv, buildMindBillReportRows, buildMindBillStatusAgingCsv, buildMindBillStatusAgingMatrix, ensureTrailingProcedureLine, formatMindBillSubmissionDate, isEmptyProcedureLine, mindBillAgingBucket, mindBillAgingDays, mindBillCustomProcedureOption, parseMindBillSubmissionDate, summarizeMindBillDashboard };
|
|
2237
|
+
export { MINDBILL_STATUS_AGING_BUCKETS, MindBillBillAgingSummaryComponent, MindBillBillLifecycleComponent, MindBillBillListComponent, MindBillBillRejectionNoticeComponent, MindBillBillSubmissionComponent, MindBillBillingDashboardComponent, MindBillBillingManagementButtonComponent, MindBillBillingReportComponent, MindBillComboBoxComponent, MindBillDateInputComponent, MindBillLifecycleStore, MindBillOrganizationOnboardingComponent, MindBillStatusAgingMatrixComponent, billRejectionIssueSummary, billRejectionIssues, buildMindBillReportCsv, buildMindBillReportRows, buildMindBillStatusAgingCsv, buildMindBillStatusAgingMatrix, ensureTrailingProcedureLine, formatMindBillSubmissionDate, isEmptyProcedureLine, mindBillAgingBucket, mindBillAgingDays, mindBillAngularAppearanceStyle, mindBillCustomProcedureOption, parseMindBillSubmissionDate, summarizeMindBillDashboard };
|
|
2041
2238
|
//# sourceMappingURL=mindbill-angular.mjs.map
|