--- 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`