feat(web): admin structure management page — /admin/structure (ATB-47) (#80)
* feat(appview): add uri field to serializeCategory (ATB-47)
* test(web): add failing tests for GET /admin/structure (ATB-47)
* feat(web): add GET /admin/structure page with category/board listing (ATB-47)
* fix(web): use lowercase method="post" on structure page forms (ATB-47)
* test(web): add failing tests for category proxy routes (ATB-47)
* feat(web): add category proxy routes for structure management (ATB-47)
* test(web): add failing tests for board proxy routes (ATB-47)
* feat(web): add board proxy routes for structure management (ATB-47)
* feat(web): add CSS for admin structure management page (ATB-47)
* test(web): add missing network error tests for edit proxy routes (ATB-47)
* fix(web): log errors when boards fetch fails per-category in structure page (ATB-47)
* docs: add completed implementation plan for ATB-47 admin structure UI
* fix(web): validate sort order and fix board delete 409 test (ATB-47)
- parseSortOrder now returns null for negative/non-integer values and
redirects with "Sort order must be a non-negative integer." error;
0 remains the default for empty/missing sort order fields
- Use Number() + Number.isInteger() instead of parseInt() to reject
floats like "1.5" that parseInt would silently truncate
- Add validation tests for negative sort order across all 4 create/edit
handlers (create category, edit category, create board, edit board)
- Fix board delete 409 test mock to use the real AppView error message
("Cannot delete board with posts. Remove all posts first.") and assert
the message appears in the redirect URL, matching category delete test