feat(sync): /api/sync/health endpoint + dashboard health pill + MALFORMED UI

Backend:
- GET /api/sync/health returns {last_sync_at, last_sync_status,
  last_halt_reason, recent_phase_failures, escalation_phase, is_healthy}.
  healthy when last run was completed (or none yet), no phase has
  tripped the 3-in-a-row escalation, and recent failures <= 1.
- Dashboard + run-level endpoints include `malformed` count so the
  Defecte pill can render.

Frontend:
- Health pill in .sync-card-controls with three states — healthy
  (success green, check icon), warning (amber, triangle), escalated
  (error red, x-octagon + glow). Tooltip exposes the halt reason and
  the top phases with recent failures.
- Status-dot + badge add MALFORMED treatment via --compare orange,
  distinct from ERROR red. DESIGN.md notes the diagnostic rationale
  (ERROR = runtime, MALFORMED = payload source issue).
- Defecte filter pill on dashboard + logs pages. Mobile segmented
  control includes Defecte count. Counts wired to the malformed key.
- startSync() shows a native confirm modal when state is
  halted_escalation — operator override still possible, not silenced.
- ORDER_STATUS.MALFORMED mirror added to shared.js.
- Cache-bust: style.css v46, shared.js v47, dashboard.js v52,
  logs.js v16.

5 endpoint tests cover empty state, completed, failed, escalated,
single-failure warning. Full CI: 257 unit + 33 e2e green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-04-22 09:14:46 +00:00
parent 41b142effb
commit bb6f3a3b87
11 changed files with 290 additions and 5 deletions

View File

@@ -19,6 +19,7 @@ const ORDER_STATUS = Object.freeze({
ERROR: 'ERROR',
CANCELLED: 'CANCELLED',
DELETED_IN_ROA: 'DELETED_IN_ROA',
MALFORMED: 'MALFORMED',
});
// ── HTML escaping ─────────────────────────────────
@@ -519,6 +520,7 @@ function orderStatusBadge(status) {
case ORDER_STATUS.ERROR: return '<span class="badge bg-danger">Eroare</span>';
case ORDER_STATUS.CANCELLED: return '<span class="badge bg-secondary">Anulat</span>';
case ORDER_STATUS.DELETED_IN_ROA: return '<span class="badge bg-dark">Sters din ROA</span>';
case ORDER_STATUS.MALFORMED: return '<span class="badge" style="background:var(--compare-light);color:var(--compare-text);border:1px solid var(--compare)">Defect</span>';
default: return `<span class="badge bg-secondary">${esc(status)}</span>`;
}
}
@@ -1037,6 +1039,8 @@ function statusDot(status) {
case ORDER_STATUS.ERROR:
case 'FAILED':
return '<span class="dot dot-red"></span>';
case ORDER_STATUS.MALFORMED:
return '<span class="dot dot-orange" title="Date defecte — escalat la GoMag"></span>';
case ORDER_STATUS.CANCELLED:
case ORDER_STATUS.DELETED_IN_ROA:
return '<span class="dot dot-gray"></span>';