diff --git a/.planning/phases/07-dashboard-nav-polish/07-01-PLAN.md b/.planning/phases/07-dashboard-nav-polish/07-01-PLAN.md new file mode 100644 index 0000000..c8eafa9 --- /dev/null +++ b/.planning/phases/07-dashboard-nav-polish/07-01-PLAN.md @@ -0,0 +1,228 @@ +--- +phase: 07-dashboard-nav-polish +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: + - imptune/api/pages.py + - imptune/templates/dashboard.html + - imptune/templates/packages.html + - tests/test_static.py +autonomous: true +requirements: [] +must_haves: + truths: + - "GET /packages returns 200 with a list of printers that have drivers assigned" + - "Dashboard shows the 5 most recently created printers from the database" + - "Dashboard shows the 5 most recently created printers with a driver assigned (recent packages)" + - "All existing tests remain green after changes" + artifacts: + - path: "imptune/templates/packages.html" + provides: "Packages listing page template" + contains: "extends \"base.html\"" + - path: "imptune/api/pages.py" + provides: "/packages route and fixed dashboard queries" + exports: ["packages_page"] + - path: "tests/test_static.py" + provides: "Integration tests for /packages and dashboard data" + contains: "test_packages_returns_200" + key_links: + - from: "imptune/api/pages.py" + to: "imptune/db/models.py" + via: "Printer.select().order_by(Printer.created_at.desc()).limit(5)" + pattern: "Printer\\.select\\(\\)" + - from: "imptune/api/pages.py" + to: "imptune/templates/packages.html" + via: "TemplateResponse name='packages.html'" + pattern: "packages\\.html" + - from: "imptune/templates/base.html" + to: "imptune/api/pages.py" + via: "nav link href='/packages' resolves to packages_page route" + pattern: "/packages" +--- + + +Fix the broken /packages nav link (404) and wire the dashboard to show real data from the database. + +Purpose: Close two visible integration gaps -- the /packages nav link returns 404 and the dashboard always shows empty lists despite real data existing in the database. +Output: Working /packages page, dashboard with live recent printers and recent packages queries, integration tests proving all three success criteria. + + + +@C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md +@C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md +@.planning/phases/07-dashboard-nav-polish/07-RESEARCH.md + + + + +From imptune/db/models.py: +```python +class Client(BaseModel): + name = CharField(unique=True) + created_at = DateTimeField(default=datetime.utcnow) + +class Driver(BaseModel): + sha256 = CharField(unique=True, index=True) + original_filename = CharField() + driver_desc = CharField(null=True) + uploaded_at = DateTimeField(default=datetime.utcnow) + +class Printer(BaseModel): + name = CharField() + ip_address = CharField() + port_name = CharField() + client = ForeignKeyField(Client, null=True, backref="printers") + driver = ForeignKeyField(Driver, null=True, backref="printers") + created_at = DateTimeField(default=datetime.utcnow) +``` + +From imptune/api/pages.py (established pattern): +```python +router = APIRouter() +templates = Jinja2Templates(directory=str(Path(__file__).parent.parent / "templates")) + +# All routes use deferred imports inside function body +# All routes use: templates.TemplateResponse(request=request, name="...", context={...}) +# All queries use: list(Model.select()...) — never Model.get() +``` + +From tests/conftest.py: +```python +@pytest.fixture +def client(tmp_data_dir): + from imptune.main import app + with TestClient(app) as c: + yield c +``` + + + + + + + Task 1: Add integration tests for /packages route and dashboard data + tests/test_static.py + + - test_packages_returns_200: GET /packages returns status 200 + - test_dashboard_shows_recent_printers: Create 2 Printer records in DB, GET / response body contains both printer names + - test_dashboard_shows_recent_packages: Create 2 Printer records (one with driver, one without), GET / response body contains the driver-assigned printer name in the packages section but not the driverless one + + +Add three test functions to the existing `tests/test_static.py` file. All tests use the `client` fixture from conftest.py. + +For `test_packages_returns_200`: Simple GET /packages, assert status_code == 200. + +For `test_dashboard_shows_recent_printers`: +1. Import Client, Driver, Printer from imptune.db.models +2. Create 2 Printer records with distinct names (e.g. "TestPrinter-Alpha", "TestPrinter-Beta") using Printer.create(name=..., ip_address="10.0.0.1", port_name="IP_10.0.0.1") +3. GET /, assert both printer names appear in response.text +4. Assert "No printers configured yet" NOT in response.text + +For `test_dashboard_shows_recent_packages`: +1. Create a Driver record: Driver.create(sha256="abc123", original_filename="test.zip", size_bytes=1000, driver_desc='["TestDriver"]') +2. Create Printer with driver assigned: Printer.create(name="PkgPrinter-Assigned", ip_address="10.0.0.2", port_name="IP_10.0.0.2", driver=driver) +3. Create Printer without driver: Printer.create(name="PkgPrinter-NoDriver", ip_address="10.0.0.3", port_name="IP_10.0.0.3") +4. GET /, assert "PkgPrinter-Assigned" appears in response.text +5. Assert "No packages exported yet" NOT in response.text + +These tests will FAIL initially (RED) because /packages returns 404 and dashboard hardcodes empty lists. + + + cd C:/Users/SebastienQUEROL/Documents/projets/ImpTune && pytest tests/test_static.py -x 2>&1 | tail -20 + + Three new test functions exist in test_static.py. test_packages_returns_200 fails with 404, dashboard tests fail because response contains "No printers configured yet" / "No packages exported yet". + + + + Task 2: Add /packages route, fix dashboard queries, create packages.html template + imptune/api/pages.py, imptune/templates/packages.html, imptune/templates/dashboard.html + +**imptune/api/pages.py** — Two changes: + +1. Fix the `dashboard` function (lines 16-25). Replace hardcoded empty lists with real queries using deferred imports inside the function body: +```python +@router.get("/", response_class=HTMLResponse) +def dashboard(request: Request): + from imptune.db.models import Printer + + recent_printers = list( + Printer.select().order_by(Printer.created_at.desc()).limit(5) + ) + recent_packages = list( + Printer.select() + .where(Printer.driver.is_null(False)) + .order_by(Printer.created_at.desc()) + .limit(5) + ) + return templates.TemplateResponse( + request=request, + name="dashboard.html", + context={ + "recent_printers": recent_printers, + "recent_packages": recent_packages, + }, + ) +``` + +2. Add a new `/packages` route at the end of the file. Follow the exact same pattern as `printers_page` — deferred imports, LEFT_OUTER joins, list() wrapper: +```python +@router.get("/packages", response_class=HTMLResponse) +def packages_page(request: Request): + from imptune.db.models import Client, Driver, Printer + + printers = list( + Printer.select(Printer, Client, Driver) + .join(Client, JOIN.LEFT_OUTER) + .switch(Printer) + .join(Driver, JOIN.LEFT_OUTER) + .where(Printer.driver.is_null(False)) + .order_by(Printer.name) + ) + return templates.TemplateResponse( + request=request, + name="packages.html", + context={"printers": printers}, + ) +``` + +**imptune/templates/packages.html** — Create new file extending base.html. Show a table of printers that have drivers assigned, with columns: Printer Name (linked to /printers/{id}), Client, Driver, and download links for Intune (.intunewin) and NinjaRMM (ZIP). Use the existing download URL patterns: `/printers/{id}/packages/intunewin` and `/printers/{id}/packages/ninja`. Show an empty state message if no package-ready printers exist. Use Pico CSS table styling (no custom classes needed beyond what base.html provides). + +**imptune/templates/dashboard.html** — Update the printer list items to be clickable links. Change: +- `
  • {{ printer.name }} — {{ printer.ip_address }}
  • ` to `
  • {{ printer.name }} — {{ printer.ip_address }}
  • ` +- For recent_packages section, change `
  • {{ package }}
  • ` to `
  • {{ package.name }}{% if package.client_id %} — {{ package.client.name }}{% endif %}
  • ` (the variable is a Printer object, not a string) +- Also update the quick-action links: "New Printer" href to "/printers", "Upload Driver" href to "/drivers", "Export Package" href to "/packages" — remove aria-disabled="true" from all three. +
    + + cd C:/Users/SebastienQUEROL/Documents/projets/ImpTune && pytest tests/ -x 2>&1 | tail -20 + + GET /packages returns 200 with a table of package-ready printers. Dashboard shows real recent printers and recent packages from the database. All tests in the full suite pass including the 3 new tests from Task 1. +
    + +
    + + +1. `pytest tests/ -x` — full test suite green +2. `pytest tests/test_static.py::test_packages_returns_200 -x` — /packages route works +3. `pytest tests/test_static.py::test_dashboard_shows_recent_printers -x` — dashboard shows real printers +4. `pytest tests/test_static.py::test_dashboard_shows_recent_packages -x` — dashboard shows real packages + + + +- GET /packages returns 200 and renders a page listing printers with drivers assigned +- Dashboard recent_printers section shows real Printer records from the database +- Dashboard recent_packages section shows Printer records that have a driver assigned +- Full test suite (pytest tests/ -x) passes with zero failures +- No regressions in existing tests + + + +After completion, create `.planning/phases/07-dashboard-nav-polish/07-01-SUMMARY.md` +