Compare commits
2 Commits
feature/cr
...
feature/da
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
deaecd8d3b | ||
| e4d4271e32 |
@@ -1,6 +1,8 @@
|
|||||||
.admin {
|
.admin {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
color: #1f2937;
|
color: #1f2937;
|
||||||
background: #f6f7f9;
|
background: #f6f7f9;
|
||||||
font-family: Arial, sans-serif;
|
font-family: Arial, sans-serif;
|
||||||
@@ -21,6 +23,11 @@
|
|||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.admin__brand-link {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
.admin__nav {
|
.admin__nav {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -34,6 +41,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.admin__main {
|
.admin__main {
|
||||||
|
flex: 1;
|
||||||
width: min(1180px, calc(100% - 32px));
|
width: min(1180px, calc(100% - 32px));
|
||||||
margin: 28px auto;
|
margin: 28px auto;
|
||||||
}
|
}
|
||||||
@@ -52,18 +60,30 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.metric {
|
.metric {
|
||||||
|
display: block;
|
||||||
padding: 18px;
|
padding: 18px;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
border: 1px solid #d9dee7;
|
border: 1px solid #d9dee7;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.metric--link {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric--link:hover {
|
||||||
|
border-color: #0f766e;
|
||||||
|
}
|
||||||
|
|
||||||
.metric__label {
|
.metric__label {
|
||||||
|
display: block;
|
||||||
color: #6b7280;
|
color: #6b7280;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.metric__value {
|
.metric__value {
|
||||||
|
display: block;
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
font-size: 28px;
|
font-size: 28px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
@@ -87,6 +107,14 @@
|
|||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.table__row {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table__row:hover {
|
||||||
|
background: #f0fdfa;
|
||||||
|
}
|
||||||
|
|
||||||
.table__cell,
|
.table__cell,
|
||||||
.table__head {
|
.table__head {
|
||||||
padding: 10px 8px;
|
padding: 10px 8px;
|
||||||
@@ -331,12 +359,22 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.stats-strip__item {
|
.stats-strip__item {
|
||||||
|
display: block;
|
||||||
padding: 14px 16px;
|
padding: 14px 16px;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
border: 1px solid #d9dee7;
|
border: 1px solid #d9dee7;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stats-strip__item--link {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-strip__item--link:hover {
|
||||||
|
border-color: #0f766e;
|
||||||
|
}
|
||||||
|
|
||||||
.stats-strip__label {
|
.stats-strip__label {
|
||||||
display: block;
|
display: block;
|
||||||
color: #6b7280;
|
color: #6b7280;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body class="admin">
|
<body class="admin">
|
||||||
<header class="admin__header">
|
<header class="admin__header">
|
||||||
<h1 class="admin__brand">MIEM Employees</h1>
|
<h1 class="admin__brand"><a class="admin__brand-link" href="/admin">MIEM Employees</a></h1>
|
||||||
<nav class="admin__nav">
|
<nav class="admin__nav">
|
||||||
<a class="admin__link" href="/admin">Обзор</a>
|
<a class="admin__link" href="/admin">Обзор</a>
|
||||||
<a class="admin__link" href="/admin/directory">Сотрудники</a>
|
<a class="admin__link" href="/admin/directory">Сотрудники</a>
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
{% block title %}Обзор · MIEM Employees{% endblock %}
|
{% block title %}Обзор · MIEM Employees{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section class="admin__grid">
|
<section class="admin__grid">
|
||||||
<div class="metric"><div class="metric__label">Всего в базе</div><div class="metric__value">{{ counts.total }}</div></div>
|
<a class="metric metric--link" href="/admin/directory"><span class="metric__label">Всего в базе</span><span class="metric__value">{{ counts.total }}</span></a>
|
||||||
<div class="metric"><div class="metric__label">Работают</div><div class="metric__value">{{ counts.active }}</div></div>
|
<a class="metric metric--link" href="/admin/directory?status=active"><span class="metric__label">Работают</span><span class="metric__value">{{ counts.active }}</span></a>
|
||||||
<div class="metric"><div class="metric__label">Новые за запуск</div><div class="metric__value">{{ counts.new_in_last_run }}</div></div>
|
<a class="metric metric--link" href="{% if latest_run %}/admin/runs/{{ latest_run.id }}#new-employees{% else %}/admin/runs{% endif %}"><span class="metric__label">Новые за запуск</span><span class="metric__value">{{ counts.new_in_last_run }}</span></a>
|
||||||
<div class="metric"><div class="metric__label">Уволены</div><div class="metric__value">{{ counts.dismissed }}</div></div>
|
<a class="metric metric--link" href="/admin/directory?status=dismissed"><span class="metric__label">Уволены</span><span class="metric__value">{{ counts.dismissed }}</span></a>
|
||||||
</section>
|
</section>
|
||||||
<section class="stats-strip">
|
<section class="stats-strip">
|
||||||
<div class="stats-strip__item">
|
<div class="stats-strip__item">
|
||||||
@@ -16,10 +16,10 @@
|
|||||||
<span class="stats-strip__value">Сотрудников пока нет</span>
|
<span class="stats-strip__value">Сотрудников пока нет</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="stats-strip__item">
|
<a class="stats-strip__item stats-strip__item--link" href="/admin/runs">
|
||||||
<span class="stats-strip__label">Запуски</span>
|
<span class="stats-strip__label">Запуски</span>
|
||||||
<span class="stats-strip__value">{{ counts.runs }}</span>
|
<span class="stats-strip__value">{{ counts.runs }}</span>
|
||||||
</div>
|
</a>
|
||||||
<div class="stats-strip__item">
|
<div class="stats-strip__item">
|
||||||
<span class="stats-strip__label">Ошибки</span>
|
<span class="stats-strip__label">Ошибки</span>
|
||||||
<span class="stats-strip__value">{{ counts.errors }}</span>
|
<span class="stats-strip__value">{{ counts.errors }}</span>
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
{% for group, title in [("new", "Новые сотрудники"), ("missing_from_source", "Потеряшки"), ("dismissed", "Уволенные")] %}
|
{% for group, title in [("new", "Новые сотрудники"), ("missing_from_source", "Потеряшки"), ("dismissed", "Уволенные")] %}
|
||||||
<section class="panel">
|
<section class="panel"{% if group == "new" %} id="new-employees"{% endif %}>
|
||||||
<h2 class="panel__title">{{ title }}</h2>
|
<h2 class="panel__title">{{ title }}</h2>
|
||||||
{% set items = run.changes[group] %}
|
{% set items = run.changes[group] %}
|
||||||
{% if items %}
|
{% if items %}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
<thead><tr><th class="table__head">ID</th><th class="table__head">Статус</th><th class="table__head">Найдено</th><th class="table__head">Обработано</th><th class="table__head">Новые</th><th class="table__head">Ошибки</th><th class="table__head">Уволены</th><th class="table__head">Старт</th></tr></thead>
|
<thead><tr><th class="table__head">ID</th><th class="table__head">Статус</th><th class="table__head">Найдено</th><th class="table__head">Обработано</th><th class="table__head">Новые</th><th class="table__head">Ошибки</th><th class="table__head">Уволены</th><th class="table__head">Старт</th></tr></thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for run in runs %}
|
{% for run in runs %}
|
||||||
<tr><td class="table__cell"><a class="admin__link" href="/admin/runs/{{ run.id }}">{{ run.id }}</a></td><td class="table__cell">{{ run.status_display }}</td><td class="table__cell">{{ run.found_count }}</td><td class="table__cell">{{ run.parsed_count }}</td><td class="table__cell">{{ run.new_count }}</td><td class="table__cell">{{ run.error_count }}</td><td class="table__cell">{{ run.dismissed_count }}</td><td class="table__cell">{{ run.started_display }}</td></tr>
|
<tr class="table__row" data-row-href="/admin/runs/{{ run.id }}"><td class="table__cell"><a class="admin__link" href="/admin/runs/{{ run.id }}">{{ run.id }}</a></td><td class="table__cell">{{ run.status_display }}</td><td class="table__cell">{{ run.found_count }}</td><td class="table__cell">{{ run.parsed_count }}</td><td class="table__cell">{{ run.new_count }}</td><td class="table__cell">{{ run.error_count }}</td><td class="table__cell">{{ run.dismissed_count }}</td><td class="table__cell">{{ run.started_display }}</td></tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
APP_VERSION = "0.4.0"
|
APP_VERSION = "0.4.1"
|
||||||
FRONTEND_VERSION = "0.4.0"
|
FRONTEND_VERSION = "0.4.1"
|
||||||
BACKEND_VERSION = "0.4.0"
|
BACKEND_VERSION = "0.4.1"
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ def test_base_navigation_is_russian_and_has_no_legacy_employees_link():
|
|||||||
assert "Сотрудники" in template
|
assert "Сотрудники" in template
|
||||||
assert "Запуски" in template
|
assert "Запуски" in template
|
||||||
assert "Выйти" in template
|
assert "Выйти" in template
|
||||||
|
assert '<a class="admin__brand-link" href="/admin">MIEM Employees</a>' in template
|
||||||
assert ">Employees<" not in template
|
assert ">Employees<" not in template
|
||||||
assert "/admin/employees" not in template
|
assert "/admin/employees" not in template
|
||||||
|
|
||||||
@@ -38,13 +39,25 @@ def test_runs_template_links_to_run_detail():
|
|||||||
template = Path("app/templates/runs.html").read_text(encoding="utf-8")
|
template = Path("app/templates/runs.html").read_text(encoding="utf-8")
|
||||||
|
|
||||||
assert 'href="/admin/runs/{{ run.id }}"' in template
|
assert 'href="/admin/runs/{{ run.id }}"' in template
|
||||||
|
assert 'data-row-href="/admin/runs/{{ run.id }}"' in template
|
||||||
|
|
||||||
|
|
||||||
def test_run_detail_template_extends_base_and_shows_change_groups():
|
def test_run_detail_template_extends_base_and_shows_change_groups():
|
||||||
template = Path("app/templates/run_detail.html").read_text(encoding="utf-8")
|
template = Path("app/templates/run_detail.html").read_text(encoding="utf-8")
|
||||||
|
|
||||||
assert '{% extends "base.html" %}' in template
|
assert '{% extends "base.html" %}' in template
|
||||||
|
assert 'id="new-employees"' in template
|
||||||
assert "Новые сотрудники" in template
|
assert "Новые сотрудники" in template
|
||||||
assert "Потеряшки" in template
|
assert "Потеряшки" in template
|
||||||
assert "Уволенные" in template
|
assert "Уволенные" in template
|
||||||
assert "Детализация сотрудников для этого запуска недоступна" in template
|
assert "Детализация сотрудников для этого запуска недоступна" in template
|
||||||
|
|
||||||
|
|
||||||
|
def test_dashboard_metric_cards_link_to_admin_targets():
|
||||||
|
template = Path("app/templates/dashboard.html").read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
assert 'href="/admin/directory"' in template
|
||||||
|
assert 'href="/admin/directory?status=active"' in template
|
||||||
|
assert '/admin/runs/{{ latest_run.id }}#new-employees' in template
|
||||||
|
assert 'href="/admin/directory?status=dismissed"' in template
|
||||||
|
assert 'href="/admin/runs"' in template
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ def test_health_returns_versions():
|
|||||||
response = client.get("/api/health")
|
response = client.get("/api/health")
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert response.json()["backend_version"] == "0.4.0"
|
assert response.json()["backend_version"] == "0.4.1"
|
||||||
|
|
||||||
|
|
||||||
def test_mcp_requires_token_and_lists_tools():
|
def test_mcp_requires_token_and_lists_tools():
|
||||||
|
|||||||
Reference in New Issue
Block a user