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:
co-authored by
Claude Sonnet 4.6
parent
ba30ada87a
commit
2fd7591eac
@@ -133,10 +133,10 @@ From backend/api/admin/__init__.py (current state):
|
|||||||
- `user_count` is `SELECT count(id) FROM users WHERE role = 'user'`.
|
- `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.
|
- `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`.
|
- `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.
|
- Response never includes raw user records, raw audit rows, password_hash, credentials_enc, totp_secret, api_key_enc, or extracted_text.
|
||||||
</behavior>
|
</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>
|
<verify>
|
||||||
<automated>cd backend && pytest tests/test_admin_overview.py -v -x</automated>
|
<automated>cd backend && pytest tests/test_admin_overview.py -v -x</automated>
|
||||||
</verify>
|
</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-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).
|
- 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.
|
- Behavior preserved: every admin CRUD/filter/download interaction continues to work after wiring in 09-04.
|
||||||
</verification>
|
</success_criteria>
|
||||||
|
|
||||||
<output>
|
<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.
|
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?**
|
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.
|
- What we know: `AppSidebar.vue` is a separate component imported by `App.vue`. `AuthLayout.vue` has no sidebar.
|
||||||
|
|||||||
Reference in New Issue
Block a user