feat(setup): print a real test slip from the printer "Test connection" modal

healthCheck only opens the transport (TCP connect / USB open) — it proves the
printer is REACHABLE, not that paper feeds and the head fires. Add a "Print test
slip" action so the admin can physically confirm a printer is live (the new
host-net USB /dev/usb/lpN path, or a network printer).

- server: POST /api/setup/test-print — printer-only, re-merges stored secrets like
  /test (so an edited network printer authenticates), creates the device, and pushes
  a short slip via the device-agnostic printReport(). Fail-soft: a print error
  (paper out, head fault, transport drop) is reported, never a 500. Mirrors the
  test-anpr pattern.
- web: testPrint() client + PrintTestResult; a button in the device modal shown for
  category=printer, with ok/fail rendering. i18n keys in sq + en (parity holds).

Server 168 tests pass; web + server typecheck clean.

Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
2026-06-27 14:36:39 +02:00
parent 045892bc94
commit 3a60367232
5 changed files with 145 additions and 0 deletions
+18
View File
@@ -350,6 +350,24 @@ export function testAnpr(driverId: string, config: DeviceConfig): Promise<AnprTe
});
}
/** Result of a physical print test: a real test slip is pushed to the printer. */
export type PrintTestResult =
| { ok: true; tookMs: number }
| { ok: false; reason: string; detail?: string; tookMs?: number };
/** Print a real test slip on the printer — without saving. Confirms the printer
* actually feeds paper + fires the head (healthCheck only opens the transport). */
export function testPrint(
driverId: string,
config: DeviceConfig,
id?: string,
): Promise<PrintTestResult> {
return apiFetch<PrintTestResult>("/api/setup/test-print", {
method: "POST",
body: JSON.stringify({ driverId, config, id }),
});
}
// --- Admin reports -------------------------------------------------------
export type ReportBucket = "hour" | "day" | "month";