Skip to content

Commit 6068bb2

Browse files
committed
dashboard, inbound messages: support deferred message status
1 parent 72790e3 commit 6068bb2

4 files changed

Lines changed: 24 additions & 5 deletions

File tree

src/ui/pages/dashboard.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ interface TickerRow {
3232
time: string;
3333
type: string;
3434
note: string;
35-
status: "ok" | "warn" | "err" | "pend";
35+
// `held` = manually deferred. Terminal status — distinct from `pend`
36+
// (worker hasn't picked up yet) so the chip doesn't read as "pending".
37+
status: "ok" | "warn" | "err" | "pend" | "held";
3638
}
3739

3840
const HARD_ERROR_STATUSES = [
@@ -121,6 +123,7 @@ function toTickerStatus(
121123
if (!status) {return "pend";}
122124
if (status === "processed" || status === "warning") {return "ok";}
123125
if (status === "code_mapping_error") {return "warn";}
126+
if (status === "deferred") {return "held";}
124127
if (status.endsWith("_error")) {return "err";}
125128
return "pend";
126129
}
@@ -385,6 +388,7 @@ function statusChip(status: TickerRow["status"]): string {
385388
if (status === "ok") {return `<span class="chip chip-ok">processed</span>`;}
386389
if (status === "warn") {return `<span class="chip chip-warn">needs mapping</span>`;}
387390
if (status === "err") {return `<span class="chip chip-err">error</span>`;}
391+
if (status === "held") {return `<span class="chip">deferred</span>`;}
388392
return `<span class="chip">pending</span>`;
389393
}
390394

src/ui/pages/inbound-detail.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ function toneChip(tone: MessageTone): string {
6767
if (tone === "ok") {return `<span class="chip chip-ok">processed</span>`;}
6868
if (tone === "warn") {return `<span class="chip chip-warn">needs mapping</span>`;}
6969
if (tone === "err") {return `<span class="chip chip-err">error</span>`;}
70+
if (tone === "held") {return `<span class="chip">deferred</span>`;}
7071
return `<span class="chip">pending</span>`;
7172
}
7273

src/ui/pages/inbound.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,11 @@ interface TypeChipCount {
5353
tone?: "accent" | "err";
5454
}
5555

56-
export type MessageTone = "ok" | "warn" | "err" | "pend";
56+
// `held` = manually deferred via POST /defer/:id. Distinct from `pend`
57+
// (received, worker hasn't processed yet — animated, terminal-bound) so the
58+
// chip doesn't show a misleading "processing" spinner on a status that won't
59+
// move on its own. Operator must POST /mark-for-retry/:id to resume.
60+
export type MessageTone = "ok" | "warn" | "err" | "pend" | "held";
5761

5862
// The 4 hard-error statuses the design's "errors" pseudo-chip aggregates.
5963
// Matches CLAUDE.md's IncomingHL7v2Message status vocabulary.
@@ -216,8 +220,9 @@ export function statusToTone(p: ParsedIncomingMessage): MessageTone {
216220
case "conversion_error":
217221
case "sending_error":
218222
return "err";
219-
case "received":
220223
case "deferred":
224+
return "held";
225+
case "received":
221226
return "pend";
222227
default:
223228
return assertNever(p);
@@ -235,6 +240,7 @@ export function statusStringToTone(s: string | undefined): MessageTone {
235240
if (!s) {return "pend";}
236241
if (s === "processed" || s === "warning") {return "ok";}
237242
if (s === "code_mapping_error") {return "warn";}
243+
if (s === "deferred") {return "held";}
238244
if (s.endsWith("_error")) {return "err";}
239245
return "pend";
240246
}
@@ -243,6 +249,10 @@ function toneDot(tone: MessageTone): string {
243249
if (tone === "ok") {return "dot ok";}
244250
if (tone === "warn") {return "dot warn";}
245251
if (tone === "err") {return "dot err";}
252+
// `held` (manually deferred): static neutral dot — terminal state, no
253+
// background work in progress. Distinct from `pend` so the row doesn't
254+
// pulse like an in-flight message.
255+
if (tone === "held") {return "dot";}
246256
// `pend` (received / queued-for-worker): animated pulse so the user
247257
// sees the message is actively being worked on, not stuck. The
248258
// in-process worker polls every ~5s so the flip to "processed" is
@@ -254,6 +264,8 @@ function toneChip(tone: MessageTone): string {
254264
if (tone === "ok") {return `<span class="chip chip-ok">processed</span>`;}
255265
if (tone === "warn") {return `<span class="chip chip-warn">needs mapping</span>`;}
256266
if (tone === "err") {return `<span class="chip chip-err">error</span>`;}
267+
// Held = neutral static chip. Terminal — no spinner, no auto-refresh.
268+
if (tone === "held") {return `<span class="chip">deferred</span>`;}
257269
// Active-state chip: inline spinner + "processing" text so the user
258270
// sees activity rather than a static "pending" label. Per-row polling
259271
// (see renderRowStatusCell) swaps this into its settled form when the

test/unit/ui/inbound.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,11 @@ describe("statusToTone", () => {
126126
expect(statusToTone(parsed({ status: "conversion_error", error: "x" }))).toBe("err");
127127
expect(statusToTone(parsed({ status: "sending_error", error: "x" }))).toBe("err");
128128
});
129-
test("maps received/deferred to pend", () => {
129+
test("maps received to pend, deferred to held", () => {
130+
// Distinct tones: `received` is animated (worker hasn't picked up yet);
131+
// `held` is static (operator deferred — terminal until POST /mark-for-retry).
130132
expect(statusToTone(parsed({ status: "received" }))).toBe("pend");
131-
expect(statusToTone(parsed({ status: "deferred" }))).toBe("pend");
133+
expect(statusToTone(parsed({ status: "deferred" }))).toBe("held");
132134
});
133135
});
134136

0 commit comments

Comments
 (0)