Compare commits

...

4 Commits

6 changed files with 37 additions and 11 deletions

View File

@@ -62,10 +62,20 @@
} }
function setupClickableRows() { function setupClickableRows() {
const openRow = (row) => {
window.location.href = row.dataset.rowHref;
};
document.querySelectorAll("[data-row-href]").forEach((row) => { document.querySelectorAll("[data-row-href]").forEach((row) => {
row.addEventListener("click", (event) => { row.addEventListener("click", (event) => {
if (event.target.closest("a, button, input, select, label")) return; if (event.target.closest("a, button, input, select, label")) return;
window.location.href = row.dataset.rowHref; openRow(row);
});
row.addEventListener("keydown", (event) => {
if (!["Enter", " "].includes(event.key)) return;
if (event.target.closest("a, button, input, select, label")) return;
event.preventDefault();
openRow(row);
}); });
}); });
} }

View File

@@ -51,7 +51,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></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></tr></thead>
<tbody> <tbody>
{% for run in runs %} {% for run in runs %}
<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.parsed_count }}</td><td class="table__cell">{{ run.error_count }}</td><td class="table__cell">{{ run.started_display }}</td></tr> <tr class="table__row" onclick="window.location.href='/admin/runs/{{ run.id }}'" onkeydown="if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); window.location.href='/admin/runs/{{ run.id }}'; }" role="link" tabindex="0"><td class="table__cell">{{ run.id }}</td><td class="table__cell">{{ run.status_display }}</td><td class="table__cell">{{ run.parsed_count }}</td><td class="table__cell">{{ run.error_count }}</td><td class="table__cell">{{ run.started_display }}</td></tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>

View File

@@ -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 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> <tr class="table__row" onclick="window.location.href='/admin/runs/{{ run.id }}'" onkeydown="if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); window.location.href='/admin/runs/{{ run.id }}'; }" role="link" tabindex="0"><td class="table__cell">{{ run.id }}</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>

View File

@@ -1,3 +1,3 @@
APP_VERSION = "0.4.2" APP_VERSION = "0.4.4"
FRONTEND_VERSION = "0.4.2" FRONTEND_VERSION = "0.4.4"
BACKEND_VERSION = "0.4.2" BACKEND_VERSION = "0.4.4"

View File

@@ -45,8 +45,12 @@ def test_dashboard_limits_latest_runs_to_five():
def test_runs_template_links_to_run_detail(): 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 'onclick="window.location.href=\'/admin/runs/{{ run.id }}\'"' in template
assert 'data-row-href="/admin/runs/{{ run.id }}"' in template assert "onkeydown=\"if (event.key === 'Enter' || event.key === ' ')" in template
assert 'role="link"' in template
assert 'tabindex="0"' in template
assert 'data-row-href="/admin/runs/{{ run.id }}"' not in template
assert '<a class="admin__link" href="/admin/runs/{{ run.id }}">' not in template
def test_run_detail_template_extends_base_and_shows_change_groups(): def test_run_detail_template_extends_base_and_shows_change_groups():
@@ -73,5 +77,17 @@ def test_dashboard_metric_cards_link_to_admin_targets():
def test_dashboard_latest_run_rows_link_to_run_detail(): def test_dashboard_latest_run_rows_link_to_run_detail():
template = Path("app/templates/dashboard.html").read_text(encoding="utf-8") template = Path("app/templates/dashboard.html").read_text(encoding="utf-8")
assert 'data-row-href="/admin/runs/{{ run.id }}"' in template assert 'onclick="window.location.href=\'/admin/runs/{{ run.id }}\'"' in template
assert 'href="/admin/runs/{{ run.id }}"' in template assert "onkeydown=\"if (event.key === 'Enter' || event.key === ' ')" in template
assert 'role="link"' in template
assert 'tabindex="0"' in template
assert 'data-row-href="/admin/runs/{{ run.id }}"' not in template
assert '<a class="admin__link" href="/admin/runs/{{ run.id }}">' not in template
def test_admin_js_supports_keyboard_activation_for_clickable_rows():
source = Path("app/static/admin.js").read_text(encoding="utf-8")
assert 'addEventListener("keydown"' in source
assert '"Enter"' in source
assert '" "' in source

View File

@@ -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.2" assert response.json()["backend_version"] == "0.4.4"
def test_mcp_requires_token_and_lists_tools(): def test_mcp_requires_token_and_lists_tools():