// Shared LOT price-quality indicator, used everywhere price_quality is
// displayed (pricelist detail page, configurator search dropdown, configurator
// table, pricing tab). price_quality is a 0-9 score set by the external pricing
// tool (see bible-local/03-database.md, qt_pricelist_items.price_quality) — QF
// only displays it, never computes it. Single source of the visual scale so
// every view stays consistent.
//
// Two render modes, chosen per client via window.QF_INDICATOR_MODE (injected by
// base.html from app_settings, key indicator_mode):
// "color" (default) — hue scale, 0 = red, 5 = yellow, 9 = green.
// "accessible" — colour-blind-safe 5-step signal-strength meter,
// single neutral hue, magnitude encoded by bar count.
// See bible-local/decisions/2026-08-31-accessible-indicator-mode.md.
(function () {
const RED = [220, 38, 38];
const YELLOW = [234, 179, 8];
const GREEN = [22, 163, 74];
function isAccessible() {
return window.QF_INDICATOR_MODE === 'accessible';
}
function lerp(c1, c2, t) {
return c1.map((v, i) => Math.round(v + (c2[i] - v) * t));
}
// Returns "r, g, b" (no rgb() wrapper) so callers can build rgb()/rgba() themselves.
function priceQualityRgb(quality) {
if (typeof quality !== 'number' || Number.isNaN(quality)) return null;
const q = Math.max(0, Math.min(9, quality));
const [r, g, b] = q <= 5 ? lerp(RED, YELLOW, q / 5) : lerp(YELLOW, GREEN, (q - 5) / 4);
return `${r}, ${g}, ${b}`;
}
function priceQualityColor(quality) {
const rgb = priceQualityRgb(quality);
return rgb ? `rgb(${rgb})` : null;
}
// 0-9 score -> 0-4 meter level. 0-1 / 2-3 / 4-5 / 6-7 / 8-9.
function priceQualityLevel(quality) {
if (typeof quality !== 'number' || Number.isNaN(quality)) return null;
const q = Math.max(0, Math.min(9, quality));
return Math.min(4, Math.floor(q / 2));
}
// Monochrome 5-step signal-strength meter. size: undefined | 'sm'.
function qualityMeterHtml(quality, opts) {
const level = priceQualityLevel(quality);
if (level === null) return '';
const size = opts && opts.size === 'sm' ? ' data-size="sm"' : '';
const title = `Качество цены: ${quality}/9`;
let bars = '';
for (let i = 0; i < 5; i++) {
bars += ``;
}
return `${bars}`;
}
// Small indicator for compact contexts (search dropdown, configurator table cell).
function qualityDotHtml(quality) {
if (isAccessible()) return qualityMeterHtml(quality, { size: 'sm' });
const color = priceQualityColor(quality);
if (!color) return '';
return ``;
}
// Indicator for a dedicated "quality" column/cell.
function qualityBadgeHtml(quality) {
if (typeof quality !== 'number') return '-';
if (isAccessible()) return qualityMeterHtml(quality, { size: 'sm' });
const color = priceQualityColor(quality);
return `${quality}`;
}
// Light row-tint background for table rows, e.g. the pricing tab. Suppressed
// in accessible mode — the pricing tab carries its own leading meter column there.
function qualityRowStyle(quality) {
if (isAccessible()) return '';
const rgb = priceQualityRgb(quality);
return rgb ? `background-color: rgba(${rgb}, 0.10)` : '';
}
window.priceQualityColor = priceQualityColor;
window.priceQualityLevel = priceQualityLevel;
window.qualityMeterHtml = qualityMeterHtml;
window.qualityDotHtml = qualityDotHtml;
window.qualityBadgeHtml = qualityBadgeHtml;
window.qualityRowStyle = qualityRowStyle;
})();