Initial commit: site monorepo with API, web, and infra.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Прогноз запаса на /feed_consumption.
|
||||
*/
|
||||
(function (global) {
|
||||
const COPY = () => global.WespAnalyticsCopy || {};
|
||||
|
||||
function escapeHtml(value) {
|
||||
return String(value ?? "")
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">");
|
||||
}
|
||||
|
||||
let forecastByComponentId = {};
|
||||
|
||||
async function fetchForecast() {
|
||||
try {
|
||||
const resp = await fetch("/api/analytics/stock-forecast");
|
||||
if (!resp.ok) return null;
|
||||
return await resp.json();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function renderBanner(data) {
|
||||
const el = document.getElementById("stockForecastBanner");
|
||||
if (!el) return;
|
||||
const text = (data && data.alertBanner) || "";
|
||||
if (!text) {
|
||||
el.hidden = true;
|
||||
el.textContent = "";
|
||||
return;
|
||||
}
|
||||
el.hidden = false;
|
||||
el.innerHTML =
|
||||
`<strong>${escapeHtml(COPY().stock?.bannerTitle || "Обратите внимание:")}</strong> ` +
|
||||
escapeHtml(text);
|
||||
}
|
||||
|
||||
function applyToCards() {
|
||||
document.querySelectorAll(".component-card[data-forecast-key], .component-card[data-component-id]").forEach((card) => {
|
||||
const key = card.getAttribute("data-forecast-key") || card.getAttribute("data-component-id");
|
||||
const fc = forecastByComponentId[key];
|
||||
if (!fc) return;
|
||||
const adjEl = card.querySelector("[data-stock-days-adjusted]");
|
||||
const explEl = card.querySelector("[data-stock-explanation]");
|
||||
const label = fc.daysLeftAdjustedLabel || fc.days_left_adjusted_label;
|
||||
if (adjEl && label) {
|
||||
adjEl.textContent = label;
|
||||
const daysVal = fc.daysLeftAdjusted != null ? fc.daysLeftAdjusted : fc.days_left_adjusted;
|
||||
adjEl.closest(".info-item")?.classList.toggle(
|
||||
"info-item--warn",
|
||||
daysVal != null && daysVal <= 2
|
||||
);
|
||||
}
|
||||
if (explEl && fc.explanation) {
|
||||
explEl.textContent = fc.explanation;
|
||||
explEl.hidden = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function refresh() {
|
||||
const data = await fetchForecast();
|
||||
forecastByComponentId = {};
|
||||
(data?.items || []).forEach((item) => {
|
||||
const key = item.forecast_key || item.component_id || item.name_key;
|
||||
if (key) forecastByComponentId[key] = item;
|
||||
});
|
||||
renderBanner(data);
|
||||
applyToCards();
|
||||
return data;
|
||||
}
|
||||
|
||||
function getForecast(componentId) {
|
||||
return forecastByComponentId[componentId] || null;
|
||||
}
|
||||
|
||||
global.WespStockForecast = { refresh, getForecast, applyToCards };
|
||||
})(typeof window !== "undefined" ? window : globalThis);
|
||||
Reference in New Issue
Block a user