fix(portal): align management block order
This commit is contained in:
@@ -319,7 +319,6 @@ function renderExecutiveDashboard(report) {
|
|||||||
const highRisk = Array.isArray(dashboard.high_risk_departments) ? dashboard.high_risk_departments : [];
|
const highRisk = Array.isArray(dashboard.high_risk_departments) ? dashboard.high_risk_departments : [];
|
||||||
const candidates = Array.isArray(dashboard.critical_candidates) ? dashboard.critical_candidates : [];
|
const candidates = Array.isArray(dashboard.critical_candidates) ? dashboard.critical_candidates : [];
|
||||||
const summary = dashboard.summary || {};
|
const summary = dashboard.summary || {};
|
||||||
const narrativeStatus = summary.risk_narrative_status || executiveDashboardStatus(dashboard);
|
|
||||||
return `
|
return `
|
||||||
<section class="card executive-dashboard-card">
|
<section class="card executive-dashboard-card">
|
||||||
<div class="section-head">
|
<div class="section-head">
|
||||||
@@ -327,11 +326,7 @@ function renderExecutiveDashboard(report) {
|
|||||||
<h3>Сводка руководителя</h3>
|
<h3>Сводка руководителя</h3>
|
||||||
<p class="muted">Что происходит в организации прямо сейчас: доверие к KPI, покрытие, риски, дела и готовность расследований.</p>
|
<p class="muted">Что происходит в организации прямо сейчас: доверие к KPI, покрытие, риски, дела и готовность расследований.</p>
|
||||||
</div>
|
</div>
|
||||||
<span class="badge ${statusClass(narrativeStatus)}">${ui(narrativeStatus)}</span>
|
<span class="badge ${statusClass(executiveDashboardStatus(dashboard))}">${ui(dashboard.forensics_readiness || "UNKNOWN")}</span>
|
||||||
</div>
|
|
||||||
<div class="list compact-list executive-summary-list">
|
|
||||||
<div class="row compact-row"><strong>Главная причина риска</strong><span class="muted">${ui(summary.main_risk_cause || "связанный риск не выражен")}</span><span class="badge ${statusClass(narrativeStatus)}">${ui(narrativeStatus)}</span></div>
|
|
||||||
<div class="row compact-row"><strong>Подтверждающие слои</strong><span class="muted">Trust KPI · Agent Coverage · Business Risk · Risk Heatmap · Security Correlation · Incident Candidates · Cases</span><span></span></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="quality-grid">
|
<div class="quality-grid">
|
||||||
<div><span class="muted">Trust KPI</span><strong>${ui(optionalPercent(dashboard.trust_kpi_score))}</strong></div>
|
<div><span class="muted">Trust KPI</span><strong>${ui(optionalPercent(dashboard.trust_kpi_score))}</strong></div>
|
||||||
@@ -350,6 +345,27 @@ function renderExecutiveDashboard(report) {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderRiskNarrative(report) {
|
||||||
|
const dashboard = report?.executive_dashboard;
|
||||||
|
const summary = dashboard?.summary || {};
|
||||||
|
const narrativeStatus = summary.risk_narrative_status || executiveDashboardStatus(dashboard || {});
|
||||||
|
return `
|
||||||
|
<section class="card risk-narrative-card">
|
||||||
|
<div class="section-head">
|
||||||
|
<div>
|
||||||
|
<h3>Связанная картина риска</h3>
|
||||||
|
<p class="muted">Главный управленческий вывод: что происходит, почему это риск и какие слои это подтверждают.</p>
|
||||||
|
</div>
|
||||||
|
<span class="badge ${statusClass(narrativeStatus)}">${ui(narrativeStatus)}</span>
|
||||||
|
</div>
|
||||||
|
<div class="list compact-list executive-summary-list">
|
||||||
|
<div class="row compact-row"><strong>Главная причина риска</strong><span class="muted">${ui(summary.main_risk_cause || "связанный риск не выражен")}</span><span class="badge ${statusClass(narrativeStatus)}">${ui(narrativeStatus)}</span></div>
|
||||||
|
<div class="row compact-row"><strong>Подтверждающие слои</strong><span class="muted">Trust KPI · Agent Coverage · Business Risk · Risk Heatmap · Security Correlation · Incident Candidates · Cases</span><span></span></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
function optionalPercent(value) {
|
function optionalPercent(value) {
|
||||||
const number = Number(value);
|
const number = Number(value);
|
||||||
return Number.isFinite(number) ? `${Math.round(number)}%` : "нет данных";
|
return Number.isFinite(number) ? `${Math.round(number)}%` : "нет данных";
|
||||||
@@ -873,6 +889,7 @@ function renderOperator(data, report) {
|
|||||||
<span class="badge ${statusClass(report?.severity)}">${escapeHtml(report?.headline || "Оперативный срез")}</span>
|
<span class="badge ${statusClass(report?.severity)}">${escapeHtml(report?.headline || "Оперативный срез")}</span>
|
||||||
</div>
|
</div>
|
||||||
${renderPeriodBanner(report)}
|
${renderPeriodBanner(report)}
|
||||||
|
${renderRiskNarrative(report)}
|
||||||
${renderExecutiveDashboard(report)}
|
${renderExecutiveDashboard(report)}
|
||||||
${renderExecutiveMetrics(report, incidents)}
|
${renderExecutiveMetrics(report, incidents)}
|
||||||
${renderAgentQuality(report?.agent_quality, report?.agent_quality_explain)}
|
${renderAgentQuality(report?.agent_quality, report?.agent_quality_explain)}
|
||||||
@@ -2130,6 +2147,7 @@ function renderReports(data) {
|
|||||||
<span class="badge ${statusClass(data.severity)}">${escapeHtml(data.severity)}</span>
|
<span class="badge ${statusClass(data.severity)}">${escapeHtml(data.severity)}</span>
|
||||||
</div>
|
</div>
|
||||||
${renderPeriodBanner(data)}
|
${renderPeriodBanner(data)}
|
||||||
|
${renderRiskNarrative(data)}
|
||||||
${renderExecutiveDashboard(data)}
|
${renderExecutiveDashboard(data)}
|
||||||
${renderReportTypeCards()}
|
${renderReportTypeCards()}
|
||||||
<div class="grid-2">
|
<div class="grid-2">
|
||||||
|
|||||||
@@ -351,6 +351,13 @@ POST /api/cases/{case_id}/status
|
|||||||
управленческий вывод: что происходит, почему это риск и какие слои это
|
управленческий вывод: что происходит, почему это риск и какие слои это
|
||||||
подтверждают.
|
подтверждают.
|
||||||
|
|
||||||
|
Такой же порядок используется в UI `/portal` и `/reports`: сначала отдельная
|
||||||
|
карточка `Связанная картина риска`, затем `Сводка руководителя`, далее
|
||||||
|
доверие к KPI/качество данных, Business Risk/Heatmap/Correlation, кандидаты,
|
||||||
|
дела и технические детали. В `/api/reports` narrative остается в существующих
|
||||||
|
обратно совместимых полях: первый `executive_points[]` и
|
||||||
|
`executive_dashboard.summary.main_risk_cause`.
|
||||||
|
|
||||||
Оперативный отчет содержит разделы:
|
Оперативный отчет содержит разделы:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ async function main() {
|
|||||||
);
|
);
|
||||||
const readyBodyText = await page.locator("#content").innerText({ timeout });
|
const readyBodyText = await page.locator("#content").innerText({ timeout });
|
||||||
const requiredExecutive = [
|
const requiredExecutive = [
|
||||||
|
"Связанная картина риска",
|
||||||
"Сотрудников в работе",
|
"Сотрудников в работе",
|
||||||
"Средний индекс активности",
|
"Средний индекс активности",
|
||||||
"Главная причина риска",
|
"Главная причина риска",
|
||||||
@@ -148,6 +149,27 @@ async function main() {
|
|||||||
ok: requiredExecutive.every((marker) => containsText(readyBodyText, marker)),
|
ok: requiredExecutive.every((marker) => containsText(readyBodyText, marker)),
|
||||||
required: requiredExecutive,
|
required: requiredExecutive,
|
||||||
});
|
});
|
||||||
|
const riskNarrativeIndex = readyBodyText.indexOf("Связанная картина риска");
|
||||||
|
const executiveIndex = readyBodyText.indexOf("Сводка руководителя");
|
||||||
|
const trustIndex = readyBodyText.indexOf("Достоверность данных агента");
|
||||||
|
const businessRiskIndex = readyBodyText.indexOf("Риски подразделений");
|
||||||
|
const candidatesIndex = readyBodyText.indexOf("Кандидаты в инциденты");
|
||||||
|
checks.push({
|
||||||
|
name: "management_block_order",
|
||||||
|
ok:
|
||||||
|
riskNarrativeIndex >= 0 &&
|
||||||
|
executiveIndex > riskNarrativeIndex &&
|
||||||
|
trustIndex > executiveIndex &&
|
||||||
|
businessRiskIndex > trustIndex &&
|
||||||
|
candidatesIndex > businessRiskIndex,
|
||||||
|
order: [
|
||||||
|
"Связанная картина риска",
|
||||||
|
"Сводка руководителя",
|
||||||
|
"Достоверность данных агента",
|
||||||
|
"Риски подразделений",
|
||||||
|
"Кандидаты в инциденты",
|
||||||
|
],
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (item.tab === "settings") {
|
if (item.tab === "settings") {
|
||||||
const requiredSettings = [
|
const requiredSettings = [
|
||||||
|
|||||||
Reference in New Issue
Block a user