diff --git a/web/index.html b/web/index.html index 676d456..f1a8510 100644 --- a/web/index.html +++ b/web/index.html @@ -78,6 +78,7 @@ /100 +
Качество данных diff --git a/web/static/app.js b/web/static/app.js index e5b89b8..6bf459d 100644 --- a/web/static/app.js +++ b/web/static/app.js @@ -1985,9 +1985,60 @@ function updateScore() { document.querySelector("#scoreHint").textContent = car ? "Короткая сводка по полноте данных и готовности к обслуживанию." : t("Добавь авто и первую запись, чтобы видеть точные отчеты"); + renderPassportVehicleSummary(car); renderScoreActions(state.vehicleScore?.missing_items || []); } +function renderPassportVehicleSummary(car) { + const root = document.querySelector("#passportVehicleSummary"); + if (!root) return; + if (!car) { + root.innerHTML = ` +
+ Выберите автомобиль + Здесь появятся номер, VIN, пробег, заправка и ближайшее ТО. +
+ `; + return; + } + const highlights = state.selectedCarHighlights?.carId === car.id ? state.selectedCarHighlights : buildCarHighlights(car, [], []); + const identity = [car.make, car.model, car.trim, car.year].filter(Boolean).join(" ") || "Паспорт без деталей"; + const plate = car.license_plate_display || car.plate_number || "номер не указан"; + const vin = car.vin || "VIN не указан"; + const oilSpec = [car.engine_oil_type, car.engine_oil_volume_l ? `${Number(car.engine_oil_volume_l).toLocaleString("ru-RU")} л` : ""] + .filter(Boolean) + .join(" · "); + const fuelAndOil = [car.fuel_type || "топливо не указано", oilSpec || "масло не указано"].join(" · "); + const rows = [ + ["Одометр", highlights.odometer], + ["Последняя заправка", highlights.lastFuel], + ["Замена масла", highlights.lastOil], + ["До следующей", highlights.oilRemaining], + ]; + root.innerHTML = ` +
+
+ ${escapeHtml(car.name)} + ${escapeHtml(identity)} +
+ Паспорт +
+
+ ${escapeHtml(plate)} + ${escapeHtml(vin)} + ${escapeHtml(fuelAndOil)} +
+
+ ${rows.map(([label, value]) => ` +
+ ${label} + ${escapeHtml(value)} +
+ `).join("")} +
+ `; +} + function scoreLabel(quality, score) { if (quality === "high_confidence" || score >= 86) return "High-confidence passport"; if (quality === "strong" || score >= 61) return "Strong profile"; diff --git a/web/static/styles.css b/web/static/styles.css index 27bf62c..009e17d 100644 --- a/web/static/styles.css +++ b/web/static/styles.css @@ -73,6 +73,10 @@ body.auth-required .shell { .passport-metric small, .passport-metric span, .passport-action span, +.passport-vehicle-empty span, +.passport-vehicle-main span, +.passport-vehicle-id, +.passport-vehicle-facts span, .achievement-card span, .timeline-item small, .timeline-empty, @@ -140,6 +144,98 @@ body.auth-required .shell { margin-top: 2px; } +.passport-vehicle-summary { + display: grid; + gap: 9px; + padding: 10px; + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 8px; + background: + linear-gradient(135deg, rgba(255, 255, 255, 0.085), rgba(255, 255, 255, 0.035)); +} + +.passport-vehicle-empty, +.passport-vehicle-main { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; +} + +.passport-vehicle-empty { + align-items: start; + flex-direction: column; + gap: 3px; +} + +.passport-vehicle-main strong, +.passport-vehicle-empty strong { + display: block; + color: #fff; + font-size: 16px; +} + +.passport-vehicle-main span { + display: block; + margin-top: 2px; + font-size: 12px; +} + +.passport-edit-link { + flex: 0 0 auto; + padding: 7px 10px; + border: 1px solid rgba(94, 224, 189, 0.3); + border-radius: 8px; + color: #b9f7e7; + font-size: 12px; + font-weight: 700; + text-decoration: none; + background: rgba(94, 224, 189, 0.08); +} + +.passport-vehicle-id { + display: flex; + flex-wrap: wrap; + gap: 6px; + font-size: 12px; +} + +.passport-vehicle-id span { + min-width: 0; + max-width: 100%; + padding: 4px 7px; + border-radius: 8px; + background: rgba(255, 255, 255, 0.06); + overflow-wrap: anywhere; +} + +.passport-vehicle-facts { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 7px; +} + +.passport-vehicle-facts div { + display: grid; + gap: 3px; + min-width: 0; + padding: 8px; + border-radius: 8px; + background: rgba(12, 20, 22, 0.34); +} + +.passport-vehicle-facts span { + font-size: 11px; + line-height: 1.2; +} + +.passport-vehicle-facts strong { + color: #fff; + font-size: 13px; + line-height: 1.25; + overflow-wrap: anywhere; +} + .passport-grid, .passport-actions, .achievement-strip { @@ -2206,6 +2302,14 @@ select { grid-template-columns: 1fr auto; } + .passport-vehicle-main { + align-items: start; + } + + .passport-vehicle-facts { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + .score-ring { width: 72px; height: 72px;