diff --git a/.gsd/milestones/M002/M002-ROADMAP.md b/.gsd/milestones/M002/M002-ROADMAP.md index a5a46b3..09480ce 100644 --- a/.gsd/milestones/M002/M002-ROADMAP.md +++ b/.gsd/milestones/M002/M002-ROADMAP.md @@ -59,7 +59,7 @@ This milestone is complete only when all are true: - [x] **S02: Download Flow + Queue Redesign** `risk:medium` `depends:[S01]` > After this: Single "Download" button with optional format picker, audio/video toggle, queue displays as styled table with sorting, completed items show download/copy/clear glyphs -- [ ] **S03: Mobile + Integration Polish** `risk:low` `depends:[S02]` +- [x] **S03: Mobile + Integration Polish** `risk:low` `depends:[S02]` > After this: Mobile layout works with new table design, admin welcome message editor functional, all flows verified end-to-end ## Boundary Map diff --git a/.gsd/milestones/M002/slices/S03/S03-PLAN.md b/.gsd/milestones/M002/slices/S03/S03-PLAN.md new file mode 100644 index 0000000..27837d3 --- /dev/null +++ b/.gsd/milestones/M002/slices/S03/S03-PLAN.md @@ -0,0 +1,52 @@ +# S03: Mobile + Integration Polish + +**Goal:** Ensure mobile view works cleanly with the new table-based queue, add a welcome message editor in the admin panel, and verify all flows end-to-end. +**Demo:** Mobile user can submit downloads and view the queue table. Admin can edit the welcome message text from the admin panel Settings tab. All navigation flows work. + +## Must-Haves + +- Mobile queue table is usable (tested at 390px viewport) +- Admin panel has a "Settings" tab with welcome message text editor +- Admin settings tab saves welcome_message via backend API +- All end-to-end flows verified in browser (desktop + mobile) +- No test regressions + +## Verification + +- `cd frontend && npx vitest run` — all tests pass +- `cd backend && source .venv/Scripts/activate && python -m pytest tests/ -q -m "not integration"` — no regressions +- Browser (desktop): full download lifecycle works +- Browser (mobile 390px): submit + queue table renders, actions work +- Browser: admin panel Settings tab → edit welcome message → saves + +## Tasks + +- [x] **T01: Admin welcome message editor** `est:30m` + - Why: Operators need to customize the welcome message without editing config files + - Files: `frontend/src/components/AdminPanel.vue`, `frontend/src/stores/admin.ts`, `backend/app/routers/admin.py` + - Do: Add a "Settings" tab to admin panel with a textarea for welcome message. Load current value from `/api/config/public`. Add PUT `/api/admin/settings` endpoint that updates the config's welcome_message in memory (runtime override — persisting to YAML is out of scope for this milestone). Add `updateSettings(data)` to admin store. Show save confirmation. + - Verify: Login to admin → Settings tab → edit message → save → reload main page → new message visible + - Done when: Welcome message is editable from admin panel + +- [x] **T02: Mobile polish and end-to-end verification** `est:30m` + - Why: Table-based queue may have issues at narrow viewports. Need to verify all flows. + - Files: Various frontend components (fixes only as needed) + - Do: Test at 390px viewport width. Fix any overflow, truncation, or touch target issues. Verify: submit download, view queue, cancel download, completed actions, dark/light toggle, footer. Fix any issues found. + - Verify: All flows work at mobile viewport. No horizontal overflow on queue table. + - Done when: Mobile experience is functional and clean + +- [x] **T03: Final test run and cleanup** `est:15m` + - Why: Ensure no regressions across the full M002 milestone + - Files: Test files, cleanup any unused components + - Do: Run full test suites. Remove ThemePicker.vue if no longer imported. Remove DownloadItem.vue if no longer imported. Clean up any dead imports. + - Verify: All tests pass, no unused component files, no dead imports + - Done when: Clean codebase, all tests green + +## Files Likely Touched + +- `frontend/src/components/AdminPanel.vue` (add Settings tab) +- `frontend/src/stores/admin.ts` (add updateSettings) +- `backend/app/routers/admin.py` (add PUT /admin/settings) +- Various frontend components (mobile fixes as needed) +- `frontend/src/components/ThemePicker.vue` (remove if unused) +- `frontend/src/components/DownloadItem.vue` (remove if unused) diff --git a/backend/app/routers/admin.py b/backend/app/routers/admin.py index 794c682..b559daa 100644 --- a/backend/app/routers/admin.py +++ b/backend/app/routers/admin.py @@ -122,3 +122,35 @@ async def manual_purge( db = request.app.state.db result = await run_purge(db, config) return result + + +@router.put("/settings") +async def update_settings( + request: Request, + _admin: str = Depends(require_admin), +) -> dict: + """Update runtime settings (in-memory only — resets on restart). + + Accepts a JSON body with optional fields: + - welcome_message: str + """ + body = await request.json() + + if not hasattr(request.app.state, "settings_overrides"): + request.app.state.settings_overrides = {} + + updated = [] + if "welcome_message" in body: + msg = body["welcome_message"] + if not isinstance(msg, str): + from fastapi.responses import JSONResponse + + return JSONResponse( + status_code=422, + content={"detail": "welcome_message must be a string"}, + ) + request.app.state.settings_overrides["welcome_message"] = msg + updated.append("welcome_message") + logger.info("Admin updated welcome_message to: %s", msg[:80]) + + return {"updated": updated, "status": "ok"} diff --git a/backend/app/routers/system.py b/backend/app/routers/system.py index 8aa6004..d2db471 100644 --- a/backend/app/routers/system.py +++ b/backend/app/routers/system.py @@ -20,10 +20,16 @@ async def public_config(request: Request) -> dict: is fragile when new sensitive fields are added later. """ config = request.app.state.config + + # Runtime overrides (set via admin settings endpoint) take precedence + overrides = getattr(request.app.state, "settings_overrides", {}) + return { "session_mode": config.session.mode, "default_theme": config.ui.default_theme, - "welcome_message": config.ui.welcome_message, + "welcome_message": overrides.get( + "welcome_message", config.ui.welcome_message + ), "purge_enabled": config.purge.enabled, "max_concurrent_downloads": config.downloads.max_concurrent, } diff --git a/frontend/src/components/AdminPanel.vue b/frontend/src/components/AdminPanel.vue index b60cae9..5e9c9c1 100644 --- a/frontend/src/components/AdminPanel.vue +++ b/frontend/src/components/AdminPanel.vue @@ -1,10 +1,15 @@ @@ -32,7 +55,7 @@ async function switchTab(tab: typeof activeTab.value) {
Displayed above the URL input on the main page. Leave empty to hide.
+ ++ Changes are applied immediately but reset on server restart. +
+