@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* Вкладка «Итоги» на /reports.
|
||||
*/
|
||||
(function (global) {
|
||||
const COPY = () => global.WespAnalyticsCopy || {};
|
||||
|
||||
function escapeHtml(value) {
|
||||
return String(value ?? "")
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">");
|
||||
}
|
||||
|
||||
function getDateParams() {
|
||||
const from = document.getElementById("date-from")?.value || "";
|
||||
const to = document.getElementById("date-to")?.value || "";
|
||||
return { date_from: from, date_to: to };
|
||||
}
|
||||
|
||||
function buildUrl() {
|
||||
const params = global.WespReportsFilters?.buildAnalyticsParams?.() || new URLSearchParams(getDateParams());
|
||||
return `/api/analytics/finance?${params.toString()}`;
|
||||
}
|
||||
|
||||
function valueToneClass(kind) {
|
||||
if (kind === "loss") return "analytics-kpi-card__value--loss";
|
||||
if (kind === "risk") return "analytics-kpi-card__value--risk";
|
||||
return "";
|
||||
}
|
||||
|
||||
function netHint(data) {
|
||||
const c = COPY().summary || {};
|
||||
if (data.dominantIssue === "overload") return c.netHintOverload || "";
|
||||
if (data.dominantIssue === "underload") return c.netHintUnderload || "";
|
||||
return c.netHintBalanced || "";
|
||||
}
|
||||
|
||||
function overloadHint(data) {
|
||||
const c = COPY().summary || {};
|
||||
if (Number(data.overloadRub) > 0) return c.overloadHint || "";
|
||||
return c.overloadHintZero || c.overloadHint || "";
|
||||
}
|
||||
|
||||
function underloadHint(data) {
|
||||
const c = COPY().summary || {};
|
||||
if (Number(data.underloadRub) > 0) return c.underloadHint || "";
|
||||
return c.underloadHintZero || c.underloadHint || "";
|
||||
}
|
||||
|
||||
function netTone(data) {
|
||||
if (data.dominantIssue === "overload") return "loss";
|
||||
if (data.dominantIssue === "underload") return "risk";
|
||||
return "";
|
||||
}
|
||||
|
||||
function renderTopBar(data) {
|
||||
const top = data.topComponents || [];
|
||||
if (!top.length) {
|
||||
return `<p class="text-muted small mb-0">${escapeHtml(COPY().summary?.empty || "")}</p>`;
|
||||
}
|
||||
const maxVal = Math.max(
|
||||
...top.map((row) => Math.max(row.overloadRub, row.underloadRub)),
|
||||
1
|
||||
);
|
||||
const rows = top
|
||||
.map((row) => {
|
||||
const isOverload = row.overloadRub > row.underloadRub;
|
||||
const main = isOverload
|
||||
? `${COPY().formatRub(row.overloadRub)} перерасход`
|
||||
: `${COPY().formatRub(row.underloadRub)} недогруз`;
|
||||
const tone = isOverload ? "analytics-top-row__value--loss" : "analytics-top-row__value--risk";
|
||||
const widthPct = Math.round(
|
||||
Math.min(100, (Math.max(row.overloadRub, row.underloadRub) / maxVal) * 1000)
|
||||
) / 10;
|
||||
return (
|
||||
`<div class="analytics-top-row">` +
|
||||
`<span class="analytics-top-row__name">${escapeHtml(row.name)}</span>` +
|
||||
`<span class="analytics-top-row__bar analytics-top-row__bar--${isOverload ? "loss" : "risk"}">` +
|
||||
`<span style="width:${widthPct}%"></span></span>` +
|
||||
`<span class="analytics-top-row__value ${tone}">${escapeHtml(main)}</span>` +
|
||||
`</div>`
|
||||
);
|
||||
})
|
||||
.join("");
|
||||
return `<div class="analytics-top-list">${rows}</div>`;
|
||||
}
|
||||
|
||||
function render(data) {
|
||||
const el = document.getElementById("analyticsSummaryList");
|
||||
if (!el) return;
|
||||
const c = COPY().summary || {};
|
||||
const overloadTone = Number(data.overloadRub) > 0 ? "loss" : "";
|
||||
const underloadTone = Number(data.underloadRub) > 0 ? "risk" : "";
|
||||
const netT = netTone(data);
|
||||
|
||||
el.innerHTML =
|
||||
`<div class="analytics-kpi-grid wesp-content-reveal">` +
|
||||
`<div class="analytics-kpi-card">` +
|
||||
`<div class="analytics-kpi-card__title">${escapeHtml(c.overloadTitle)}</div>` +
|
||||
`<div class="analytics-kpi-card__value ${valueToneClass(overloadTone)}">${escapeHtml(COPY().formatRub(data.overloadRub))}</div>` +
|
||||
`<div class="analytics-kpi-card__hint">${escapeHtml(overloadHint(data))}</div>` +
|
||||
`</div>` +
|
||||
`<div class="analytics-kpi-card">` +
|
||||
`<div class="analytics-kpi-card__title">${escapeHtml(c.underloadTitle)}</div>` +
|
||||
`<div class="analytics-kpi-card__value ${valueToneClass(underloadTone)}">${escapeHtml(COPY().formatRub(data.underloadRub))}</div>` +
|
||||
`<div class="analytics-kpi-card__hint">${escapeHtml(underloadHint(data))}</div>` +
|
||||
`</div>` +
|
||||
`<div class="analytics-kpi-card">` +
|
||||
`<div class="analytics-kpi-card__title">${escapeHtml(c.netTitle)}</div>` +
|
||||
`<div class="analytics-kpi-card__value ${valueToneClass(netT)}">${escapeHtml(COPY().formatRub(Math.abs(data.netRub)))}</div>` +
|
||||
`<div class="analytics-kpi-card__hint">${escapeHtml(netHint(data))}</div>` +
|
||||
`</div>` +
|
||||
`</div>` +
|
||||
`<div class="analytics-top-block zt-section wesp-content-reveal">` +
|
||||
`<h3 class="analytics-section-title">${escapeHtml(c.topTitle)}</h3>` +
|
||||
renderTopBar(data) +
|
||||
`</div>` +
|
||||
`<div class="analytics-links wesp-content-reveal">` +
|
||||
`<button type="button" class="btn btn-link btn-sm p-0" data-analytics-goto-alerts>${escapeHtml(c.allAlertsLink)}</button>` +
|
||||
`</div>`;
|
||||
el.querySelector("[data-analytics-goto-alerts]")?.addEventListener("click", () => {
|
||||
global.WespFeedAlerts?.openJournalAlerts?.();
|
||||
});
|
||||
global.WespZootechModals?.revealContent(el);
|
||||
}
|
||||
|
||||
async function load() {
|
||||
const el = document.getElementById("analyticsSummaryList");
|
||||
if (!el) return;
|
||||
el.innerHTML =
|
||||
'<div class="loading-state wesp-content-reveal"><div class="zt-spinner" role="status"></div>' +
|
||||
'<p class="zt-loading-caption">Загрузка итогов…</p></div>';
|
||||
try {
|
||||
const resp = await fetch(buildUrl());
|
||||
if (!resp.ok) throw new Error("Не удалось загрузить итоги");
|
||||
render(await resp.json());
|
||||
} catch (err) {
|
||||
el.innerHTML = `<div class="empty-state zt-empty"><p class="zt-empty__hint">${escapeHtml(err.message)}</p></div>`;
|
||||
}
|
||||
}
|
||||
|
||||
function onFiltersApplied() {
|
||||
if (global.WespFeedAlerts?.getCurrentView?.() === "summary") load();
|
||||
}
|
||||
|
||||
function init() {
|
||||
/* экспорт — WespReportsExport */
|
||||
}
|
||||
|
||||
global.WespAnalyticsSummary = { init, load, onFiltersApplied };
|
||||
})(typeof window !== "undefined" ? window : globalThis);
|
||||
Reference in New Issue
Block a user