docs(09): fix plan checker warnings — ORDER BY desc, closed tag, resolved marker

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
curo1305
2026-06-12 15:40:31 +02:00
co-authored by Claude Sonnet 4.6
parent ba30ada87a
commit 2fd7591eac
3 changed files with 4 additions and 4 deletions
@@ -133,10 +133,10 @@ From backend/api/admin/__init__.py (current state):
- `user_count` is `SELECT count(id) FROM users WHERE role = 'user'`.
- `total_storage_bytes` is `SELECT sum(used_bytes) FROM quotas` coerced to `0` when NULL.
- `doc_status` is `{status: count}` from `SELECT status, count(id) FROM documents GROUP BY status`.
- `recent_audit` is the result of `_build_filtered_query_with_handles(None, None, None, None).limit(10)` mapped via `_audit_to_dict_with_handles`.
- `recent_audit` is the result of `_build_filtered_query_with_handles(None, None, None, None).order_by(AuditLog.created_at.desc()).limit(10)` mapped via `_audit_to_dict_with_handles` — the `.order_by(AuditLog.created_at.desc())` must be chained before `.limit(10)` to guarantee newest-first ordering that the test asserts.
- Response never includes raw user records, raw audit rows, password_hash, credentials_enc, totp_secret, api_key_enc, or extracted_text.
</behavior>
<action>Create `backend/api/admin/overview.py`. Imports: `from __future__ import annotations`; `from fastapi import APIRouter, Depends`; `from sqlalchemy import func, select`; `from sqlalchemy.ext.asyncio import AsyncSession`; `from db.models import Document, Quota, User`; `from deps.auth import get_current_admin`; `from deps.db import get_db`; `from api.audit import _build_filtered_query_with_handles, _audit_to_dict_with_handles`. Declare `router = APIRouter()` with NO prefix (single WHY comment: "NO prefix — parent `__init__.py` carries `/api/admin` (D-02)"). Declare `@router.get("/overview")` async function `get_overview(session: AsyncSession = Depends(get_db), _admin: User = Depends(get_current_admin)) -> dict`. Compute user_count via `await session.scalar(select(func.count(User.id)).where(User.role == "user"))` (default to 0 if None). Compute total_storage_bytes via `await session.scalar(select(func.sum(Quota.used_bytes)))` (default 0). Compute doc_status via `(await session.execute(select(Document.status, func.count(Document.id)).group_by(Document.status))).all()` and convert to dict. Compute recent_audit by calling `_build_filtered_query_with_handles(None, None, None, None).limit(10)`, executing, then mapping each row tuple via `_audit_to_dict_with_handles(*row)` — confirm the row unpacking shape against the audit.py source (the existing list endpoint already does this — copy that exact iteration). Return the dict with the four keys. Modify `backend/api/admin/__init__.py`: add `from api.admin.overview import router as overview_router` alongside existing imports (preserve the docstring WHY comment); append `router.include_router(overview_router)` after the existing three `include_router` calls. Then remove the `@pytest.mark.xfail` decorators from all 8 tests in `backend/tests/test_admin_overview.py` so they execute as real tests.</action>
<action>Create `backend/api/admin/overview.py`. Imports: `from __future__ import annotations`; `from fastapi import APIRouter, Depends`; `from sqlalchemy import func, select`; `from sqlalchemy.ext.asyncio import AsyncSession`; `from db.models import Document, Quota, User`; `from deps.auth import get_current_admin`; `from deps.db import get_db`; `from api.audit import _build_filtered_query_with_handles, _audit_to_dict_with_handles`. Declare `router = APIRouter()` with NO prefix (single WHY comment: "NO prefix — parent `__init__.py` carries `/api/admin` (D-02)"). Declare `@router.get("/overview")` async function `get_overview(session: AsyncSession = Depends(get_db), _admin: User = Depends(get_current_admin)) -> dict`. Compute user_count via `await session.scalar(select(func.count(User.id)).where(User.role == "user"))` (default to 0 if None). Compute total_storage_bytes via `await session.scalar(select(func.sum(Quota.used_bytes)))` (default 0). Compute doc_status via `(await session.execute(select(Document.status, func.count(Document.id)).group_by(Document.status))).all()` and convert to dict. Compute recent_audit by calling `_build_filtered_query_with_handles(None, None, None, None).order_by(AuditLog.created_at.desc()).limit(10)`, executing, then mapping each row tuple via `_audit_to_dict_with_handles(*row)` — confirm the row unpacking shape against the audit.py source (the existing list endpoint already does this — copy that exact iteration). Return the dict with the four keys. Modify `backend/api/admin/__init__.py`: add `from api.admin.overview import router as overview_router` alongside existing imports (preserve the docstring WHY comment); append `router.include_router(overview_router)` after the existing three `include_router` calls. Then remove the `@pytest.mark.xfail` decorators from all 8 tests in `backend/tests/test_admin_overview.py` so they execute as real tests.</action>
<verify>
<automated>cd backend && pytest tests/test_admin_overview.py -v -x</automated>
</verify>
@@ -167,7 +167,7 @@ Verify before extracting each file: `grep -E "^import" frontend/src/components/a
- ADMIN-08: Four standalone admin view files exist under `frontend/src/views/admin/`.
- ADMIN-10: Each view is a self-contained component that the router can mount as a child of `/admin` (router wiring lands in 09-04).
- Behavior preserved: every admin CRUD/filter/download interaction continues to work after wiring in 09-04.
</verification>
</success_criteria>
<output>
Create `.planning/phases/09-admin-panel-rearchitecture/09-03-SUMMARY.md` when done. List the four new files, any dead-prop removals, and any orphaned-emit-to-fetch replacements made during extraction.
@@ -668,7 +668,7 @@ router.include_router(overview_router)
---
## Open Questions
## Open Questions (RESOLVED)
1. **AdminSidebar — separate component or inline in AdminLayout?**
- What we know: `AppSidebar.vue` is a separate component imported by `App.vue`. `AuthLayout.vue` has no sidebar.