fix: show employee news once in a collapsed section
CI / test (pull_request) Successful in 10m45s
CI / Fingerprint diagnostics (production deployment temporarily disabled) (pull_request) Skipped

This commit is contained in:
codex committed 2026-10-28 18:31:15 +03:00
1 parent fd3008dfde
commit cdf879fdf5
7 files changed
+72 -11

No files matched your search

+14
View File
@@ -281,6 +281,20 @@
border-radius: 8px;
}
.employee-card__news-toggle {
min-height: 44px;
align-content: center;
cursor: pointer;
}
.employee-card__news-toggle .employee-section__title {
display: inline;
}
.employee-card__section--news[open] .employee-card__news-toggle {
margin-bottom: 12px;
}
.employee-card__meta {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
+6 -5
View File
@@ -106,8 +106,8 @@
{% endif %}
{% if employee_view.news_links %}
<section class="employee-card__section">
<h3 class="employee-section__title">В новостях</h3>
<details class="employee-card__section employee-card__section--news">
<summary class="employee-card__news-toggle"><h3 class="employee-section__title">В новостях</h3></summary>
<ul class="employee-card__list">
{% for news in employee_view.news_links %}
<li class="employee-card__list-item">
@@ -121,14 +121,15 @@
</li>
{% endfor %}
</ul>
</section>
</details>
{% endif %}
<section class="employee-card__section">
<h3 class="employee-section__title">Разделы профиля</h3>
{% if employee_view.sections %}
{% set profile_sections = employee_view.sections | rejectattr('type', 'equalto', 'news') | list %}
{% if profile_sections %}
<div class="employee-card__sections">
{% for section in employee_view.sections %}
{% for section in profile_sections %}
<article class="employee-section">
<div class="employee-section__header">
<h4 class="employee-section__title">{{ section.title }}</h4>
+3 -3
View File
@@ -1,3 +1,3 @@
APP_VERSION = "1.5.0"
FRONTEND_VERSION = "1.5.0"
BACKEND_VERSION = "1.5.0"
APP_VERSION = "1.5.1"
FRONTEND_VERSION = "1.5.1"
BACKEND_VERSION = "1.5.1"
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "miem-workers"
version = "1.5.0"
version = "1.5.1"
description = "MIEM employees parser, admin API, and web admin"
requires-python = ">=3.11"
dependencies = [
+1 -1
View File
@@ -18,7 +18,7 @@ def test_health_returns_versions():
response = TestClient(app).get("/api/health")
assert response.status_code == 200
assert response.json()["backend_version"] == "1.5.0"
assert response.json()["backend_version"] == "1.5.1"
def test_api_employees_and_stats_require_admin_session():
+1 -1
View File
@@ -230,7 +230,7 @@ def test_public_page_data_minimization_and_safe_links(db_session, export):
assert "javascript:" not in fragment.text and "private@example.org" not in fragment.text
assert "javascript:" not in response.text and "private@example.org" not in response.text
assert "/admin/employees/" not in response.text and "/admin/logout" not in response.text
assert "Backend 1.5.0 · Frontend 1.5.0" in response.text
assert "Backend 1.5.1 · Frontend 1.5.1" in response.text
assert "Вход в систему" in response.text
assert client.get("/admin/directory", follow_redirects=False).status_code in (302, 303, 401, 403)
assert "Сотрудники не найдены" in client.get("/admin/council?q=missing").text
+46
View File
@@ -1,5 +1,13 @@
from pathlib import Path
import pytest
from bs4 import BeautifulSoup
from jinja2 import Environment, FileSystemLoader
from starlette.requests import Request
from app.models import Employee, EmployeeNewsLink
from app.services.admin_data import employee_detail_payload
def test_employee_detail_template_is_human_readable():
template = Path("app/templates/employee_detail.html").read_text(encoding="utf-8")
@@ -33,3 +41,41 @@ def test_employee_detail_template_is_human_readable():
assert "Обновить данные" in template
assert 'action="/admin/employees/{{ employee.id }}/refresh"' in template
assert "Снапшоты" in template
@pytest.mark.parametrize("news_source", [None, "snapshot", "stored"])
@pytest.mark.parametrize("has_other_section", [False, True])
def test_employee_news_render_once_in_closed_disclosure(db_session, news_source, has_other_section):
sections = [{"title": "Биография", "type": "generic", "paragraphs": ["Текст биографии"]}] if has_other_section else []
if news_source:
sections.append({"title": "В новостях", "type": "news",
"news_links": [{"title": "Новость из профиля", "url": "https://example.test/snapshot"}],
"paragraphs": ["Дублирующий текст новостей"]})
employee = Employee(profile_key="staff:news", canonical_url="https://example.test/profile",
full_name="Тестовый сотрудник", status="active", current_data={"sections": sections})
db_session.add(employee)
db_session.flush()
if news_source == "stored":
db_session.add(EmployeeNewsLink(employee_id=employee.id, title="Сохранённая новость",
url="https://example.test/stored", summary="Описание новости", source_hash="a" * 64))
db_session.flush()
template = Environment(loader=FileSystemLoader("app/templates"), autoescape=True).get_template("employee_detail.html")
rendered = template.render(employee=employee, employee_view=employee_detail_payload(employee), snapshots=[],
request=Request({"type": "http", "scheme": "http", "headers": [],
"path": f"/admin/employees/{employee.id}"}))
soup = BeautifulSoup(rendered, "html.parser")
assert not any(badge.get_text(strip=True) == "news" for badge in soup.select(".employee-section__type"))
assert "Дублирующий текст новостей" not in rendered
assert ("Текст биографии" in rendered) == has_other_section
assert ("Разделы профиля не найдены." in rendered) == (not has_other_section)
news = soup.select("details.employee-card__section--news")
assert len(news) == (1 if news_source else 0)
if news_source:
assert not news[0].has_attr("open")
assert news[0].summary.h3.get_text(strip=True) == "В новостях"
expected_url = f"https://example.test/{'stored' if news_source == 'stored' else 'snapshot'}"
assert [link["href"] for link in news[0].select("a")] == [expected_url]
assert len(soup.select(f'a[href="{expected_url}"]')) == 1
if news_source == "stored":
assert "Описание новости" in news[0].get_text()
assert "Новость из профиля" not in rendered