diff --git a/app/web/account.py b/app/web/account.py index 45d1026..9713a1a 100644 --- a/app/web/account.py +++ b/app/web/account.py @@ -7,7 +7,7 @@ from app.auth.csrf import ensure_csrf_token, validate_csrf from app.db.repositories import ActivityRepository, SyncRunRepository, UserRepository from app.security.credentials import CredentialCipher from app.sync.manager import SyncAlreadyRunning -from app.web.operations import _normalize_outcome +from app.web.operations import _outcome_toast, _toast_html from app.web.routes import templates router = APIRouter() @@ -92,11 +92,19 @@ async def account_sync(request: Request, csrf_token: str = Form(...)): validate_csrf(request, csrf_token) try: outcome = await request.app.state.sync_manager.sync_user(user_id) + with request.app.state.session_factory() as session: + user = UserRepository(session).get(user_id) + label = user.name if user is not None else "Account" + message, level = _outcome_toast(outcome, label) except SyncAlreadyRunning: - return HTMLResponse("Sync already running for this user", status_code=409) - return templates.TemplateResponse( - request, "fragments/sync_result.html", {"outcomes": [_normalize_outcome(outcome)]} + with request.app.state.session_factory() as session: + user = UserRepository(session).get(user_id) + label = user.name if user is not None else "Account" + message, level = f"{label}: sync already running", "info" + status_html = ( + templates.get_template("fragments/account_status.html").render(user=user) if user is not None else "" ) + return HTMLResponse(status_html + _toast_html(message, level)) @router.get("/account/edit", response_class=HTMLResponse) diff --git a/app/web/templates/account/detail.html b/app/web/templates/account/detail.html index 3a0c4c4..2ed3796 100644 --- a/app/web/templates/account/detail.html +++ b/app/web/templates/account/detail.html @@ -6,7 +6,8 @@

{{ user.name }}

Edit -
+
@@ -17,19 +18,7 @@
-
-
Status
-
{{ user.health_state.value.replace("_", " ") }}
- -
MyWhoosh state
-
{{ user.mywhoosh_state }}
- -
Garmin state
-
{{ user.garmin_state }}
- -
Action reason
-
{{ user.action_reason or "-" }}
-
+ {% include "fragments/account_status.html" %}
{% if user.action_reason == "mywhoosh_device_conflict" %} diff --git a/app/web/templates/fragments/account_status.html b/app/web/templates/fragments/account_status.html new file mode 100644 index 0000000..a78d008 --- /dev/null +++ b/app/web/templates/fragments/account_status.html @@ -0,0 +1,13 @@ +
+
Status
+
{{ user.health_state.value.replace("_", " ") }}
+ +
MyWhoosh state
+
{{ user.mywhoosh_state }}
+ +
Garmin state
+
{{ user.garmin_state }}
+ +
Action reason
+
{{ user.action_reason or "-" }}
+
diff --git a/tests/web/test_account_web.py b/tests/web/test_account_web.py index 82c4f1c..a65a248 100644 --- a/tests/web/test_account_web.py +++ b/tests/web/test_account_web.py @@ -289,7 +289,7 @@ def test_account_sync_triggers_own_user_only(app, client: TestClient, fake_sync_ assert fake_sync_manager.user_calls == [user_id] -def test_account_sync_reports_already_running(app, client: TestClient, fake_sync_manager) -> None: +def test_account_sync_reports_already_running_as_toast(app, client: TestClient, fake_sync_manager) -> None: create_user_via_admin(client) account_login(client, email="max@mywhoosh.example", password="mw-secret") app.state.sync_manager = fake_sync_manager @@ -299,10 +299,25 @@ def test_account_sync_reports_already_running(app, client: TestClient, fake_sync csrf = extract_csrf(page.text) response = client.post("/account/sync", data={"csrf_token": csrf}) - assert response.status_code == 409 + assert response.status_code == 200 assert "already running" in response.text.lower() +def test_account_sync_updates_status_block_and_shows_toast(app, client: TestClient, fake_sync_manager) -> None: + create_user_via_admin(client) + account_login(client, email="max@mywhoosh.example", password="mw-secret") + app.state.sync_manager = fake_sync_manager + + page = client.get("/account") + csrf = extract_csrf(page.text) + response = client.post("/account/sync", data={"csrf_token": csrf}) + + assert response.status_code == 200 + assert 'id="account-status"' in response.text + assert 'hx-swap-oob="true"' in response.text + assert "0 imported, 0 failed" in response.text + + def test_account_sync_requires_login(client: TestClient) -> None: response = client.post("/account/sync", data={"csrf_token": "whatever"}, follow_redirects=False) assert response.status_code == 303