Three content defects found by a live reviewer:
- Dashboard attention subtext was raw, untranslated evidence.summary text, and for
11 of 15 seeded issues that text was literally "Synthetic deterministic seed
issue". AttentionItem now exposes evidence_signals (stable code + params, same
shape as the issue detail page) instead of a detail string; the frontend renders
them through a shared describeEvidenceSignal() used by both the dashboard and the
issue detail page. Every previously-placeholder seed row now cites a real,
per-rule-type fact (a genuinely crossed service threshold, a genuinely blank
field, or a real pair of booking odometer readings) instead of invented prose.
- 5 of 7 blocked vehicles had no quality issue at all and one had only a resolved
one, so "needs attention" led nowhere. Each now has a real open
missing_required_field issue backed by a genuinely blank field (no schema change,
no migration -- reuses the existing data-quality pipeline).
- Booking odometer fields showing a bare "-" for 25 reserved + 1 active booking now
show a localized explanation ("trip hasn't started yet" / "not yet closed").
MO-024's rented-but-service-overdue contradiction was already caught by the
vehicle-status evaluator (DQ-SCAN, vehicle.manual_review_required) -- added a
regression test rather than new logic.
Also fixed a related bug the above exposed: the vehicle entity_snapshot omitted
registration_number entirely, so the "provide missing fields" form always showed
it blank regardless of the real value.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
43 lines
1.9 KiB
TypeScript
43 lines
1.9 KiB
TypeScript
import type { EvidenceSignal } from "../api/types";
|
|
|
|
type TFunction = (key: string, options?: Record<string, unknown>) => string;
|
|
|
|
// The backend never emits prose for data-quality evidence -- only stable signal codes
|
|
// plus raw data params (see backend/app/services/data_quality.py::_open_issue). This is
|
|
// the one place that turns them into the operator's selected language, shared by the
|
|
// issue detail page and the dashboard attention queue so the same code always reads the
|
|
// same way everywhere it appears.
|
|
export function describeEvidenceSignal(
|
|
t: TFunction,
|
|
formatNumber: (value: number) => string,
|
|
signal: EvidenceSignal,
|
|
): string {
|
|
const { code, params = {} } = signal;
|
|
if (code.startsWith("vehicle.")) {
|
|
return t(`quality:detail.vehicleStatusConflict.reasonCodes.${code}`, { defaultValue: code });
|
|
}
|
|
switch (code) {
|
|
case "missing_field":
|
|
return t("quality:detail.evidence.missingField", {
|
|
field: t(`quality:detail.missingField.fields.${params.field}`, { defaultValue: String(params.field) }),
|
|
});
|
|
case "overlap.reserved_bookings":
|
|
return t("quality:detail.evidence.overlapReservedBookings", {
|
|
refs: Array.isArray(params.refs) ? params.refs.join(" ↔ ") : "",
|
|
});
|
|
case "odometer.regression":
|
|
return t("quality:detail.evidence.odometerRegression", {
|
|
laterRef: params.later_ref,
|
|
laterKm: formatNumber(Number(params.later_km ?? 0)),
|
|
earlierRef: params.earlier_ref,
|
|
earlierKm: formatNumber(Number(params.earlier_km ?? 0)),
|
|
});
|
|
case "duplicate.similar_name":
|
|
return t("quality:detail.evidence.duplicateSimilarName", { score: params.score });
|
|
case "attention.upcoming_booking_missing_inspection":
|
|
return t("quality:detail.evidence.upcomingBookingMissingInspection", { bookingRef: params.booking_ref });
|
|
default:
|
|
return t(`quality:detail.evidence.${code}`, { defaultValue: code });
|
|
}
|
|
}
|